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-05-01 03:47:44 +04:00
|
|
|
#include "clang/Basic/LangOptions.h"
|
2008-05-02 03:13:35 +04:00
|
|
|
#include "clang/Basic/SourceManager.h"
|
2009-02-14 04:43:44 +03:00
|
|
|
#include "clang/Analysis/PathSensitive/GRExprEngineBuilders.h"
|
2008-08-17 07:20:02 +04:00
|
|
|
#include "clang/Analysis/PathSensitive/GRStateTrait.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"
|
2009-02-14 06:16:10 +03:00
|
|
|
#include "clang/Analysis/PathSensitive/SymbolManager.h"
|
2008-08-11 09:35:13 +04:00
|
|
|
#include "clang/AST/DeclObjC.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-10-21 19:53:15 +04:00
|
|
|
#include "llvm/ADT/ImmutableList.h"
|
2008-05-07 22:36:45 +04:00
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2008-04-09 05:10:13 +04:00
|
|
|
#include "llvm/Support/Compiler.h"
|
2008-05-16 22:33:44 +04:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2008-03-11 22:44:10 +03:00
|
|
|
#include <ostream>
|
2008-08-13 00:41:56 +04:00
|
|
|
#include <stdarg.h>
|
2008-03-06 03:08:09 +03:00
|
|
|
|
|
|
|
using namespace clang;
|
2008-10-25 01:18:08 +04:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Utility functions.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
// The "fundamental rule" for naming conventions of methods:
|
|
|
|
// (url broken into two lines)
|
|
|
|
// http://developer.apple.com/documentation/Cocoa/Conceptual/
|
|
|
|
// MemoryMgmt/Tasks/MemoryManagementRules.html
|
|
|
|
//
|
|
|
|
// "You take ownership of an object if you create it using a method whose name
|
|
|
|
// begins with “alloc” or “new” or contains “copy” (for example, alloc,
|
|
|
|
// newObject, or mutableCopy), or if you send it a retain message. You are
|
|
|
|
// responsible for relinquishing ownership of objects you own using release
|
|
|
|
// or autorelease. Any other time you receive an object, you must
|
|
|
|
// not release it."
|
|
|
|
//
|
2009-02-21 08:13:43 +03:00
|
|
|
|
|
|
|
using llvm::CStrInCStrNoCase;
|
2009-02-21 21:26:02 +03:00
|
|
|
using llvm::StringsEqualNoCase;
|
2009-02-21 08:13:43 +03:00
|
|
|
|
|
|
|
enum NamingConvention { NoConvention, CreateRule, InitRule };
|
|
|
|
|
|
|
|
static inline bool isWordEnd(char ch, char prev, char next) {
|
|
|
|
return ch == '\0'
|
|
|
|
|| (islower(prev) && isupper(ch)) // xxxC
|
|
|
|
|| (isupper(prev) && isupper(ch) && islower(next)) // XXCreate
|
|
|
|
|| !isalpha(ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline const char* parseWord(const char* s) {
|
|
|
|
char ch = *s, prev = '\0';
|
|
|
|
assert(ch != '\0');
|
|
|
|
char next = *(s+1);
|
|
|
|
while (!isWordEnd(ch, prev, next)) {
|
|
|
|
prev = ch;
|
|
|
|
ch = next;
|
|
|
|
next = *((++s)+1);
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static NamingConvention deriveNamingConvention(const char* s) {
|
|
|
|
// A method/function name may contain a prefix. We don't know it is there,
|
|
|
|
// however, until we encounter the first '_'.
|
|
|
|
bool InPossiblePrefix = true;
|
|
|
|
bool AtBeginning = true;
|
|
|
|
NamingConvention C = NoConvention;
|
|
|
|
|
|
|
|
while (*s != '\0') {
|
|
|
|
// Skip '_'.
|
|
|
|
if (*s == '_') {
|
|
|
|
if (InPossiblePrefix) {
|
|
|
|
InPossiblePrefix = false;
|
|
|
|
AtBeginning = true;
|
|
|
|
// Discard whatever 'convention' we
|
|
|
|
// had already derived since it occurs
|
|
|
|
// in the prefix.
|
|
|
|
C = NoConvention;
|
|
|
|
}
|
|
|
|
++s;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip numbers, ':', etc.
|
|
|
|
if (!isalpha(*s)) {
|
|
|
|
++s;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *wordEnd = parseWord(s);
|
|
|
|
assert(wordEnd > s);
|
|
|
|
unsigned len = wordEnd - s;
|
|
|
|
|
|
|
|
switch (len) {
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
// Methods starting with 'new' follow the create rule.
|
2009-02-21 21:26:02 +03:00
|
|
|
if (AtBeginning && StringsEqualNoCase("new", s, len))
|
2009-02-21 08:13:43 +03:00
|
|
|
C = CreateRule;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
// Methods starting with 'alloc' or contain 'copy' follow the
|
|
|
|
// create rule
|
2009-02-21 21:26:02 +03:00
|
|
|
if ((AtBeginning && StringsEqualNoCase("alloc", s, len)) ||
|
2009-02-22 10:32:24 +03:00
|
|
|
(C == NoConvention && StringsEqualNoCase("copy", s, len)))
|
2009-02-21 08:13:43 +03:00
|
|
|
C = CreateRule;
|
|
|
|
else // Methods starting with 'init' follow the init rule.
|
2009-02-21 21:26:02 +03:00
|
|
|
if (AtBeginning && StringsEqualNoCase("init", s, len))
|
2009-02-21 08:13:43 +03:00
|
|
|
C = InitRule;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we aren't in the prefix and have a derived convention then just
|
|
|
|
// return it now.
|
|
|
|
if (!InPossiblePrefix && C != NoConvention)
|
|
|
|
return C;
|
|
|
|
|
|
|
|
AtBeginning = false;
|
|
|
|
s = wordEnd;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We will get here if there wasn't more than one word
|
|
|
|
// after the prefix.
|
|
|
|
return C;
|
|
|
|
}
|
|
|
|
|
2008-10-25 01:18:08 +04:00
|
|
|
static bool followsFundamentalRule(const char* s) {
|
2009-02-21 08:13:43 +03:00
|
|
|
return deriveNamingConvention(s) == CreateRule;
|
2008-11-05 19:54:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool followsReturnRule(const char* s) {
|
2009-02-21 08:13:43 +03:00
|
|
|
NamingConvention C = deriveNamingConvention(s);
|
|
|
|
return C == CreateRule || C == InitRule;
|
2008-11-05 19:54:44 +03:00
|
|
|
}
|
2008-10-25 01:18:08 +04:00
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-06-26 01:21:56 +04:00
|
|
|
// Selector creation functions.
|
2008-04-17 22:12:53 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-05-01 22:31:44 +04:00
|
|
|
static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) {
|
2008-04-17 22:12:53 +04:00
|
|
|
IdentifierInfo* II = &Ctx.Idents.get(name);
|
|
|
|
return Ctx.Selectors.getSelector(0, &II);
|
|
|
|
}
|
|
|
|
|
2008-05-06 04:30:21 +04:00
|
|
|
static inline Selector GetUnarySelector(const char* name, ASTContext& Ctx) {
|
|
|
|
IdentifierInfo* II = &Ctx.Idents.get(name);
|
|
|
|
return Ctx.Selectors.getSelector(1, &II);
|
|
|
|
}
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Type querying functions.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2009-01-13 00:45:02 +03:00
|
|
|
static bool hasPrefix(const char* s, const char* prefix) {
|
|
|
|
if (!prefix)
|
|
|
|
return true;
|
2008-05-08 00:06:41 +04:00
|
|
|
|
2009-01-13 00:45:02 +03:00
|
|
|
char c = *s;
|
|
|
|
char cP = *prefix;
|
2008-05-08 00:06:41 +04:00
|
|
|
|
2009-01-13 00:45:02 +03:00
|
|
|
while (c != '\0' && cP != '\0') {
|
|
|
|
if (c != cP) break;
|
|
|
|
c = *(++s);
|
|
|
|
cP = *(++prefix);
|
|
|
|
}
|
2008-05-08 00:06:41 +04:00
|
|
|
|
2009-01-13 00:45:02 +03:00
|
|
|
return cP == '\0';
|
2008-05-08 00:06:41 +04:00
|
|
|
}
|
|
|
|
|
2009-01-13 00:45:02 +03:00
|
|
|
static bool hasSuffix(const char* s, const char* suffix) {
|
|
|
|
const char* loc = strstr(s, suffix);
|
|
|
|
return loc && strcmp(suffix, loc) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isRefType(QualType RetTy, const char* prefix,
|
|
|
|
ASTContext* Ctx = 0, const char* name = 0) {
|
2008-07-15 20:50:12 +04:00
|
|
|
|
2009-01-13 00:45:02 +03:00
|
|
|
if (TypedefType* TD = dyn_cast<TypedefType>(RetTy.getTypePtr())) {
|
|
|
|
const char* TDName = TD->getDecl()->getIdentifier()->getName();
|
|
|
|
return hasPrefix(TDName, prefix) && hasSuffix(TDName, "Ref");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Ctx || !name)
|
2008-07-15 20:50:12 +04:00
|
|
|
return false;
|
2009-01-13 00:45:02 +03:00
|
|
|
|
|
|
|
// Is the type void*?
|
|
|
|
const PointerType* PT = RetTy->getAsPointerType();
|
|
|
|
if (!(PT->getPointeeType().getUnqualifiedType() == Ctx->VoidTy))
|
2008-07-15 20:50:12 +04:00
|
|
|
return false;
|
2009-01-13 00:45:02 +03:00
|
|
|
|
|
|
|
// Does the name start with the prefix?
|
|
|
|
return hasPrefix(name, prefix);
|
2008-07-15 20:50:12 +04:00
|
|
|
}
|
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-06-26 01:21:56 +04:00
|
|
|
// Primitives used for constructing summaries for function/method calls.
|
2008-04-10 03:49:11 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
namespace {
|
|
|
|
/// ArgEffect is used to summarize a function/method call's effect on a
|
|
|
|
/// particular argument.
|
2009-02-18 21:54:33 +03:00
|
|
|
enum ArgEffect { IncRefMsg, IncRef,
|
|
|
|
DecRefMsg, DecRef,
|
2009-02-19 00:57:45 +03:00
|
|
|
MakeCollectable,
|
2009-02-18 21:54:33 +03:00
|
|
|
DoNothing, DoNothingByRef,
|
2008-07-09 22:11:16 +04:00
|
|
|
StopTracking, MayEscape, SelfOwn, Autorelease };
|
2008-06-26 01:21:56 +04:00
|
|
|
|
|
|
|
/// ArgEffects summarizes the effects of a function/method call on all of
|
|
|
|
/// its arguments.
|
|
|
|
typedef std::vector<std::pair<unsigned,ArgEffect> > ArgEffects;
|
2008-03-11 09:39:11 +03:00
|
|
|
}
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
namespace llvm {
|
2008-06-26 01:21:56 +04:00
|
|
|
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(I->first);
|
|
|
|
ID.AddInteger((unsigned) I->second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2008-03-11 09:39:11 +03:00
|
|
|
} // end llvm namespace
|
|
|
|
|
|
|
|
namespace {
|
2008-06-26 01:21:56 +04:00
|
|
|
|
|
|
|
/// RetEffect is used to summarize a function/method call's behavior with
|
|
|
|
/// respect to its return value.
|
|
|
|
class VISIBILITY_HIDDEN RetEffect {
|
2008-03-11 09:39:11 +03:00
|
|
|
public:
|
2008-06-23 22:02:52 +04:00
|
|
|
enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol,
|
|
|
|
NotOwnedSymbol, ReceiverAlias };
|
2009-01-28 08:56:51 +03:00
|
|
|
|
|
|
|
enum ObjKind { CF, ObjC, AnyObj };
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
private:
|
2009-01-28 08:56:51 +03:00
|
|
|
Kind K;
|
|
|
|
ObjKind O;
|
|
|
|
unsigned index;
|
|
|
|
|
|
|
|
RetEffect(Kind k, unsigned idx = 0) : K(k), O(AnyObj), index(idx) {}
|
|
|
|
RetEffect(Kind k, ObjKind o) : K(k), O(o), index(0) {}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
public:
|
2009-01-28 08:56:51 +03:00
|
|
|
Kind getKind() const { return K; }
|
|
|
|
|
|
|
|
ObjKind getObjKind() const { return O; }
|
2008-06-26 01:21:56 +04:00
|
|
|
|
|
|
|
unsigned getIndex() const {
|
2008-03-11 09:39:11 +03:00
|
|
|
assert(getKind() == Alias);
|
2009-01-28 08:56:51 +03:00
|
|
|
return index;
|
2008-03-11 09:39:11 +03:00
|
|
|
}
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
static RetEffect MakeAlias(unsigned Idx) {
|
|
|
|
return RetEffect(Alias, Idx);
|
|
|
|
}
|
|
|
|
static RetEffect MakeReceiverAlias() {
|
|
|
|
return RetEffect(ReceiverAlias);
|
|
|
|
}
|
2009-01-28 08:56:51 +03:00
|
|
|
static RetEffect MakeOwned(ObjKind o, bool isAllocated = false) {
|
|
|
|
return RetEffect(isAllocated ? OwnedAllocatedSymbol : OwnedSymbol, o);
|
2008-06-26 01:21:56 +04:00
|
|
|
}
|
2009-01-28 08:56:51 +03:00
|
|
|
static RetEffect MakeNotOwned(ObjKind o) {
|
|
|
|
return RetEffect(NotOwnedSymbol, o);
|
2008-06-26 01:21:56 +04:00
|
|
|
}
|
|
|
|
static RetEffect MakeNoRet() {
|
|
|
|
return RetEffect(NoRet);
|
2008-06-23 22:02:52 +04:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
void Profile(llvm::FoldingSetNodeID& ID) const {
|
2009-01-28 08:56:51 +03:00
|
|
|
ID.AddInteger((unsigned)K);
|
|
|
|
ID.AddInteger((unsigned)O);
|
|
|
|
ID.AddInteger(index);
|
2008-06-26 01:21:56 +04:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
};
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
|
|
|
|
class VISIBILITY_HIDDEN RetainSummary : public llvm::FoldingSetNode {
|
2008-05-06 19:44:25 +04:00
|
|
|
/// Args - an ordered vector of (index, ArgEffect) pairs, where index
|
|
|
|
/// specifies the argument (starting from 0). This can be sparsely
|
|
|
|
/// populated; arguments with no entry in Args use 'DefaultArgEffect'.
|
2008-03-11 09:39:11 +03:00
|
|
|
ArgEffects* Args;
|
2008-05-06 19:44:25 +04:00
|
|
|
|
|
|
|
/// DefaultArgEffect - The default ArgEffect to apply to arguments that
|
|
|
|
/// do not have an entry in Args.
|
|
|
|
ArgEffect DefaultArgEffect;
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// Receiver - If this summary applies to an Objective-C message expression,
|
|
|
|
/// this is the effect applied to the state of the receiver.
|
2008-05-06 06:26:56 +04:00
|
|
|
ArgEffect Receiver;
|
2008-06-26 01:21:56 +04:00
|
|
|
|
|
|
|
/// Ret - The effect on the return value. Used to indicate if the
|
|
|
|
/// function/method call returns a new tracked symbol, returns an
|
|
|
|
/// alias of one of the arguments in the call, and so on.
|
2008-03-11 09:39:11 +03:00
|
|
|
RetEffect Ret;
|
2008-06-26 01:21:56 +04:00
|
|
|
|
2008-07-18 21:24:20 +04:00
|
|
|
/// EndPath - Indicates that execution of this method/function should
|
|
|
|
/// terminate the simulation of a path.
|
|
|
|
bool EndPath;
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
public:
|
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
RetainSummary(ArgEffects* A, RetEffect R, ArgEffect defaultEff,
|
2008-07-18 21:24:20 +04:00
|
|
|
ArgEffect ReceiverEff, bool endpath = false)
|
|
|
|
: Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R),
|
|
|
|
EndPath(endpath) {}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// getArg - Return the argument effect on the argument specified by
|
|
|
|
/// idx (starting from 0).
|
2008-03-11 20:48:22 +03:00
|
|
|
ArgEffect getArg(unsigned idx) const {
|
2008-05-06 19:44:25 +04:00
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
if (!Args)
|
2008-05-06 19:44:25 +04:00
|
|
|
return DefaultArgEffect;
|
2008-04-24 21:22:33 +04:00
|
|
|
|
|
|
|
// 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
|
2008-06-26 01:21:56 +04:00
|
|
|
// argument they actually modify with respect to the reference count.
|
2008-04-24 21:22:33 +04:00
|
|
|
for (ArgEffects::reverse_iterator I=Args->rbegin(), E=Args->rend();
|
|
|
|
I!=E; ++I) {
|
|
|
|
|
|
|
|
if (idx > I->first)
|
2008-05-06 19:44:25 +04:00
|
|
|
return DefaultArgEffect;
|
2008-04-24 21:22:33 +04:00
|
|
|
|
|
|
|
if (idx == I->first)
|
|
|
|
return I->second;
|
|
|
|
}
|
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
return DefaultArgEffect;
|
2008-03-11 20:48:22 +03:00
|
|
|
}
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// getRetEffect - Returns the effect on the return value of the call.
|
2008-05-06 06:26:56 +04:00
|
|
|
RetEffect getRetEffect() const {
|
2008-03-12 04:21:45 +03:00
|
|
|
return Ret;
|
|
|
|
}
|
|
|
|
|
2008-07-18 21:24:20 +04:00
|
|
|
/// isEndPath - Returns true if executing the given method/function should
|
|
|
|
/// terminate the path.
|
|
|
|
bool isEndPath() const { return EndPath; }
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// getReceiverEffect - Returns the effect on the receiver of the call.
|
|
|
|
/// This is only meaningful if the summary applies to an ObjCMessageExpr*.
|
2008-05-06 06:26:56 +04:00
|
|
|
ArgEffect getReceiverEffect() const {
|
|
|
|
return Receiver;
|
|
|
|
}
|
|
|
|
|
This patch is motivated by numerous strict-aliasing warnings when compiling
clang as a Release build.
The big change is that all AST nodes (subclasses of Stmt) whose children are
Expr* store their children as Stmt* or arrays of Stmt*. This is to remove
strict-aliasing warnings when using StmtIterator. None of the interfaces of any
of the classes have changed (except those with arg_iterators, see below), as the
accessor methods introduce the needed casts (via cast<>). While this extra
casting may seem cumbersome, it actually adds some important sanity checks
throughout the codebase, as clients using StmtIterator can potentially overwrite
children that are expected to be Expr* with Stmt* (that aren't Expr*). The casts
provide extra sanity checks that are operational in debug builds to catch
invariant violations such as these.
For classes that have arg_iterators (e.g., CallExpr), the definition of
arg_iterator has been replaced. Instead of it being Expr**, it is an actual
class (called ExprIterator) that wraps a Stmt**, and provides the necessary
operators for iteration. The nice thing about this class is that it also uses
cast<> to type-checking, which introduces extra sanity checks throughout the
codebase that are useful for debugging.
A few of the CodeGen functions that use arg_iterator (especially from
OverloadExpr) have been modified to take begin and end iterators instead of a
base Expr** and the number of arguments. This matches more with the abstraction
of iteration. This still needs to be cleaned up a little bit, as clients expect
that ExprIterator is a RandomAccessIterator (which we may or may not wish to
allow for efficiency of representation).
This is a fairly large patch. It passes the tests (except CodeGen/bitfield.c,
which was already broken) on both a Debug and Release build, but it should
obviously be reviewed.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@52378 91177308-0d34-0410-b5e6-96231b3b80d8
2008-06-17 06:43:46 +04:00
|
|
|
typedef ArgEffects::const_iterator ExprIterator;
|
2008-03-11 09:39:11 +03:00
|
|
|
|
This patch is motivated by numerous strict-aliasing warnings when compiling
clang as a Release build.
The big change is that all AST nodes (subclasses of Stmt) whose children are
Expr* store their children as Stmt* or arrays of Stmt*. This is to remove
strict-aliasing warnings when using StmtIterator. None of the interfaces of any
of the classes have changed (except those with arg_iterators, see below), as the
accessor methods introduce the needed casts (via cast<>). While this extra
casting may seem cumbersome, it actually adds some important sanity checks
throughout the codebase, as clients using StmtIterator can potentially overwrite
children that are expected to be Expr* with Stmt* (that aren't Expr*). The casts
provide extra sanity checks that are operational in debug builds to catch
invariant violations such as these.
For classes that have arg_iterators (e.g., CallExpr), the definition of
arg_iterator has been replaced. Instead of it being Expr**, it is an actual
class (called ExprIterator) that wraps a Stmt**, and provides the necessary
operators for iteration. The nice thing about this class is that it also uses
cast<> to type-checking, which introduces extra sanity checks throughout the
codebase that are useful for debugging.
A few of the CodeGen functions that use arg_iterator (especially from
OverloadExpr) have been modified to take begin and end iterators instead of a
base Expr** and the number of arguments. This matches more with the abstraction
of iteration. This still needs to be cleaned up a little bit, as clients expect
that ExprIterator is a RandomAccessIterator (which we may or may not wish to
allow for efficiency of representation).
This is a fairly large patch. It passes the tests (except CodeGen/bitfield.c,
which was already broken) on both a Debug and Release build, but it should
obviously be reviewed.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@52378 91177308-0d34-0410-b5e6-96231b3b80d8
2008-06-17 06:43:46 +04:00
|
|
|
ExprIterator begin_args() const { return Args->begin(); }
|
|
|
|
ExprIterator end_args() const { return Args->end(); }
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects* A,
|
2008-05-06 19:44:25 +04:00
|
|
|
RetEffect RetEff, ArgEffect DefaultEff,
|
2008-07-18 21:39:56 +04:00
|
|
|
ArgEffect ReceiverEff, bool EndPath) {
|
2008-03-11 09:39:11 +03:00
|
|
|
ID.AddPointer(A);
|
2008-05-06 06:26:56 +04:00
|
|
|
ID.Add(RetEff);
|
2008-05-06 19:44:25 +04:00
|
|
|
ID.AddInteger((unsigned) DefaultEff);
|
2008-05-06 06:26:56 +04:00
|
|
|
ID.AddInteger((unsigned) ReceiverEff);
|
2008-07-18 21:39:56 +04:00
|
|
|
ID.AddInteger((unsigned) EndPath);
|
2008-03-11 09:39:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void Profile(llvm::FoldingSetNodeID& ID) const {
|
2008-07-18 21:39:56 +04:00
|
|
|
Profile(ID, Args, Ret, DefaultArgEffect, Receiver, EndPath);
|
2008-03-11 09:39:11 +03:00
|
|
|
}
|
|
|
|
};
|
2008-06-24 03:30:29 +04:00
|
|
|
} // end anonymous namespace
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Data structures for constructing summaries.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-06-24 03:30:29 +04:00
|
|
|
namespace {
|
2008-06-26 01:21:56 +04:00
|
|
|
class VISIBILITY_HIDDEN ObjCSummaryKey {
|
|
|
|
IdentifierInfo* II;
|
|
|
|
Selector S;
|
|
|
|
public:
|
|
|
|
ObjCSummaryKey(IdentifierInfo* ii, Selector s)
|
|
|
|
: II(ii), S(s) {}
|
|
|
|
|
|
|
|
ObjCSummaryKey(ObjCInterfaceDecl* d, Selector s)
|
|
|
|
: II(d ? d->getIdentifier() : 0), S(s) {}
|
|
|
|
|
|
|
|
ObjCSummaryKey(Selector s)
|
|
|
|
: II(0), S(s) {}
|
|
|
|
|
|
|
|
IdentifierInfo* getIdentifier() const { return II; }
|
|
|
|
Selector getSelector() const { return S; }
|
|
|
|
};
|
2008-06-24 03:30:29 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace llvm {
|
2008-06-26 01:21:56 +04:00
|
|
|
template <> struct DenseMapInfo<ObjCSummaryKey> {
|
|
|
|
static inline ObjCSummaryKey getEmptyKey() {
|
|
|
|
return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getEmptyKey(),
|
|
|
|
DenseMapInfo<Selector>::getEmptyKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline ObjCSummaryKey getTombstoneKey() {
|
|
|
|
return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getTombstoneKey(),
|
|
|
|
DenseMapInfo<Selector>::getTombstoneKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned getHashValue(const ObjCSummaryKey &V) {
|
|
|
|
return (DenseMapInfo<IdentifierInfo*>::getHashValue(V.getIdentifier())
|
|
|
|
& 0x88888888)
|
|
|
|
| (DenseMapInfo<Selector>::getHashValue(V.getSelector())
|
|
|
|
& 0x55555555);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) {
|
|
|
|
return DenseMapInfo<IdentifierInfo*>::isEqual(LHS.getIdentifier(),
|
|
|
|
RHS.getIdentifier()) &&
|
|
|
|
DenseMapInfo<Selector>::isEqual(LHS.getSelector(),
|
|
|
|
RHS.getSelector());
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isPod() {
|
|
|
|
return DenseMapInfo<ObjCInterfaceDecl*>::isPod() &&
|
|
|
|
DenseMapInfo<Selector>::isPod();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end llvm namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
class VISIBILITY_HIDDEN ObjCSummaryCache {
|
|
|
|
typedef llvm::DenseMap<ObjCSummaryKey, RetainSummary*> MapTy;
|
|
|
|
MapTy M;
|
|
|
|
public:
|
|
|
|
ObjCSummaryCache() {}
|
|
|
|
|
|
|
|
typedef MapTy::iterator iterator;
|
|
|
|
|
|
|
|
iterator find(ObjCInterfaceDecl* D, Selector S) {
|
|
|
|
|
|
|
|
// Do a lookup with the (D,S) pair. If we find a match return
|
|
|
|
// the iterator.
|
|
|
|
ObjCSummaryKey K(D, S);
|
|
|
|
MapTy::iterator I = M.find(K);
|
|
|
|
|
|
|
|
if (I != M.end() || !D)
|
|
|
|
return I;
|
|
|
|
|
|
|
|
// Walk the super chain. If we find a hit with a parent, we'll end
|
|
|
|
// up returning that summary. We actually allow that key (null,S), as
|
|
|
|
// we cache summaries for the null ObjCInterfaceDecl* to allow us to
|
|
|
|
// generate initial summaries without having to worry about NSObject
|
|
|
|
// being declared.
|
|
|
|
// FIXME: We may change this at some point.
|
|
|
|
for (ObjCInterfaceDecl* C=D->getSuperClass() ;; C=C->getSuperClass()) {
|
|
|
|
if ((I = M.find(ObjCSummaryKey(C, S))) != M.end())
|
|
|
|
break;
|
2008-06-24 03:30:29 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
if (!C)
|
|
|
|
return I;
|
2008-06-24 03:30:29 +04:00
|
|
|
}
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
// Cache the summary with original key to make the next lookup faster
|
|
|
|
// and return the iterator.
|
|
|
|
M[K] = I->second;
|
|
|
|
return I;
|
|
|
|
}
|
|
|
|
|
2008-08-13 00:41:56 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
iterator find(Expr* Receiver, Selector S) {
|
|
|
|
return find(getReceiverDecl(Receiver), S);
|
|
|
|
}
|
|
|
|
|
|
|
|
iterator find(IdentifierInfo* II, Selector S) {
|
|
|
|
// FIXME: Class method lookup. Right now we dont' have a good way
|
|
|
|
// of going between IdentifierInfo* and the class hierarchy.
|
|
|
|
iterator I = M.find(ObjCSummaryKey(II, S));
|
|
|
|
return I == M.end() ? M.find(ObjCSummaryKey(S)) : I;
|
|
|
|
}
|
|
|
|
|
|
|
|
ObjCInterfaceDecl* getReceiverDecl(Expr* E) {
|
2008-06-24 03:30:29 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
const PointerType* PT = E->getType()->getAsPointerType();
|
|
|
|
if (!PT) return 0;
|
|
|
|
|
|
|
|
ObjCInterfaceType* OI = dyn_cast<ObjCInterfaceType>(PT->getPointeeType());
|
|
|
|
if (!OI) return 0;
|
2008-06-24 03:30:29 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
return OI ? OI->getDecl() : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
iterator end() { return M.end(); }
|
|
|
|
|
|
|
|
RetainSummary*& operator[](ObjCMessageExpr* ME) {
|
|
|
|
|
|
|
|
Selector S = ME->getSelector();
|
|
|
|
|
|
|
|
if (Expr* Receiver = ME->getReceiver()) {
|
|
|
|
ObjCInterfaceDecl* OD = getReceiverDecl(Receiver);
|
|
|
|
return OD ? M[ObjCSummaryKey(OD->getIdentifier(), S)] : M[S];
|
2008-06-24 03:30:29 +04:00
|
|
|
}
|
2008-06-26 01:21:56 +04:00
|
|
|
|
|
|
|
return M[ObjCSummaryKey(ME->getClassName(), S)];
|
|
|
|
}
|
|
|
|
|
|
|
|
RetainSummary*& operator[](ObjCSummaryKey K) {
|
|
|
|
return M[K];
|
|
|
|
}
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
RetainSummary*& operator[](Selector S) {
|
|
|
|
return M[ ObjCSummaryKey(S) ];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Data structures for managing collections of summaries.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-06-24 03:30:29 +04:00
|
|
|
namespace {
|
2008-06-26 01:21:56 +04:00
|
|
|
class VISIBILITY_HIDDEN RetainSummaryManager {
|
2008-05-06 02:11:16 +04:00
|
|
|
|
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
// Typedefs.
|
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
|
|
|
|
typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<ArgEffects> >
|
|
|
|
ArgEffectsSetTy;
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
typedef llvm::FoldingSet<RetainSummary>
|
|
|
|
SummarySetTy;
|
|
|
|
|
|
|
|
typedef llvm::DenseMap<FunctionDecl*, RetainSummary*>
|
|
|
|
FuncSummariesTy;
|
|
|
|
|
2008-06-24 03:30:29 +04:00
|
|
|
typedef ObjCSummaryCache ObjCMethodSummariesTy;
|
2008-05-06 02:11:16 +04:00
|
|
|
|
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
// Data.
|
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// Ctx - The ASTContext object for the analyzed ASTs.
|
2008-04-29 09:33:51 +04:00
|
|
|
ASTContext& Ctx;
|
2008-07-01 21:21:27 +04:00
|
|
|
|
2008-07-09 22:11:16 +04:00
|
|
|
/// CFDictionaryCreateII - An IdentifierInfo* representing the indentifier
|
|
|
|
/// "CFDictionaryCreate".
|
|
|
|
IdentifierInfo* CFDictionaryCreateII;
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// GCEnabled - Records whether or not the analyzed code runs in GC mode.
|
2008-04-29 09:33:51 +04:00
|
|
|
const bool GCEnabled;
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// SummarySet - A FoldingSet of uniqued summaries.
|
2008-04-11 02:58:08 +04:00
|
|
|
SummarySetTy SummarySet;
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// FuncSummaries - A map from FunctionDecls to summaries.
|
2008-05-06 02:11:16 +04:00
|
|
|
FuncSummariesTy FuncSummaries;
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// ObjCClassMethodSummaries - A map from selectors (for instance methods)
|
|
|
|
/// to summaries.
|
2008-06-24 02:21:20 +04:00
|
|
|
ObjCMethodSummariesTy ObjCClassMethodSummaries;
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// ObjCMethodSummaries - A map from selectors to summaries.
|
2008-06-24 02:21:20 +04:00
|
|
|
ObjCMethodSummariesTy ObjCMethodSummaries;
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// ArgEffectsSet - A FoldingSet of uniqued ArgEffects.
|
2008-05-06 02:11:16 +04:00
|
|
|
ArgEffectsSetTy ArgEffectsSet;
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects,
|
|
|
|
/// and all other data used by the checker.
|
2008-05-06 02:11:16 +04:00
|
|
|
llvm::BumpPtrAllocator BPAlloc;
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// ScratchArgs - A holding buffer for construct ArgEffects.
|
2008-05-06 02:11:16 +04:00
|
|
|
ArgEffects ScratchArgs;
|
|
|
|
|
2008-05-06 22:11:36 +04:00
|
|
|
RetainSummary* StopSummary;
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
// Methods.
|
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// getArgEffects - Returns a persistent ArgEffects object based on the
|
|
|
|
/// data in ScratchArgs.
|
2008-03-12 04:21:45 +03:00
|
|
|
ArgEffects* getArgEffects();
|
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable };
|
2008-10-23 05:56:15 +04:00
|
|
|
|
|
|
|
public:
|
2009-01-13 00:45:02 +03:00
|
|
|
RetainSummary* getUnarySummary(FunctionType* FT, UnaryFuncKind func);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary* getCFSummaryCreateRule(FunctionDecl* FD);
|
|
|
|
RetainSummary* getCFSummaryGetRule(FunctionDecl* FD);
|
2009-01-13 00:45:02 +03:00
|
|
|
RetainSummary* getCFCreateGetRuleSummary(FunctionDecl* FD, const char* FName);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
RetainSummary* getPersistentSummary(ArgEffects* AE, RetEffect RetEff,
|
2008-05-06 19:44:25 +04:00
|
|
|
ArgEffect ReceiverEff = DoNothing,
|
2008-07-18 21:24:20 +04:00
|
|
|
ArgEffect DefaultEff = MayEscape,
|
|
|
|
bool isEndPath = false);
|
2008-10-29 07:07:07 +03:00
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
RetainSummary* getPersistentSummary(RetEffect RE,
|
2008-05-06 19:44:25 +04:00
|
|
|
ArgEffect ReceiverEff = DoNothing,
|
2008-05-22 21:31:13 +04:00
|
|
|
ArgEffect DefaultEff = MayEscape) {
|
2008-05-06 19:44:25 +04:00
|
|
|
return getPersistentSummary(getArgEffects(), RE, ReceiverEff, DefaultEff);
|
2008-05-06 04:30:21 +04:00
|
|
|
}
|
2008-05-06 03:55:01 +04:00
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
RetainSummary* getPersistentStopSummary() {
|
2008-05-06 22:11:36 +04:00
|
|
|
if (StopSummary)
|
|
|
|
return StopSummary;
|
|
|
|
|
|
|
|
StopSummary = getPersistentSummary(RetEffect::MakeNoRet(),
|
|
|
|
StopTracking, StopTracking);
|
2008-10-29 07:07:07 +03:00
|
|
|
|
2008-05-06 22:11:36 +04:00
|
|
|
return StopSummary;
|
2008-05-06 19:44:25 +04:00
|
|
|
}
|
2008-05-06 08:20:12 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
RetainSummary* getInitMethodSummary(ObjCMessageExpr* ME);
|
2008-05-06 03:55:01 +04:00
|
|
|
|
2008-06-24 02:21:20 +04:00
|
|
|
void InitializeClassMethodSummaries();
|
|
|
|
void InitializeMethodSummaries();
|
2008-10-23 05:56:15 +04:00
|
|
|
|
2009-01-07 03:39:56 +03:00
|
|
|
bool isTrackedObjectType(QualType T);
|
|
|
|
|
2008-10-23 05:56:15 +04:00
|
|
|
private:
|
|
|
|
|
2008-07-18 21:24:20 +04:00
|
|
|
void addClsMethSummary(IdentifierInfo* ClsII, Selector S,
|
|
|
|
RetainSummary* Summ) {
|
|
|
|
ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
|
|
|
|
}
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
void addNSObjectClsMethSummary(Selector S, RetainSummary *Summ) {
|
|
|
|
ObjCClassMethodSummaries[S] = Summ;
|
|
|
|
}
|
|
|
|
|
|
|
|
void addNSObjectMethSummary(Selector S, RetainSummary *Summ) {
|
|
|
|
ObjCMethodSummaries[S] = Summ;
|
|
|
|
}
|
|
|
|
|
2008-08-12 22:48:50 +04:00
|
|
|
void addInstMethSummary(const char* Cls, RetainSummary* Summ, va_list argp) {
|
2008-08-12 22:30:56 +04:00
|
|
|
|
|
|
|
IdentifierInfo* ClsII = &Ctx.Idents.get(Cls);
|
|
|
|
llvm::SmallVector<IdentifierInfo*, 10> II;
|
2008-07-18 21:24:20 +04:00
|
|
|
|
2008-08-12 22:30:56 +04:00
|
|
|
while (const char* s = va_arg(argp, const char*))
|
|
|
|
II.push_back(&Ctx.Idents.get(s));
|
|
|
|
|
|
|
|
Selector S = Ctx.Selectors.getSelector(II.size(), &II[0]);
|
2008-07-18 21:24:20 +04:00
|
|
|
ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
|
|
|
|
}
|
2008-08-12 22:48:50 +04:00
|
|
|
|
|
|
|
void addInstMethSummary(const char* Cls, RetainSummary* Summ, ...) {
|
|
|
|
va_list argp;
|
|
|
|
va_start(argp, Summ);
|
|
|
|
addInstMethSummary(Cls, Summ, argp);
|
|
|
|
va_end(argp);
|
|
|
|
}
|
2008-08-12 22:30:56 +04:00
|
|
|
|
|
|
|
void addPanicSummary(const char* Cls, ...) {
|
|
|
|
RetainSummary* Summ = getPersistentSummary(0, RetEffect::MakeNoRet(),
|
|
|
|
DoNothing, DoNothing, true);
|
|
|
|
va_list argp;
|
|
|
|
va_start (argp, Cls);
|
2008-08-12 22:48:50 +04:00
|
|
|
addInstMethSummary(Cls, Summ, argp);
|
2008-08-12 22:30:56 +04:00
|
|
|
va_end(argp);
|
|
|
|
}
|
2008-07-18 21:24:20 +04:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
public:
|
2008-05-06 02:11:16 +04:00
|
|
|
|
|
|
|
RetainSummaryManager(ASTContext& ctx, bool gcenabled)
|
2008-07-01 21:21:27 +04:00
|
|
|
: Ctx(ctx),
|
2008-07-09 22:11:16 +04:00
|
|
|
CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")),
|
2008-06-26 01:21:56 +04:00
|
|
|
GCEnabled(gcenabled), StopSummary(0) {
|
|
|
|
|
|
|
|
InitializeClassMethodSummaries();
|
|
|
|
InitializeMethodSummaries();
|
|
|
|
}
|
2008-04-29 09:33:51 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
~RetainSummaryManager();
|
|
|
|
|
2008-06-24 07:56:45 +04:00
|
|
|
RetainSummary* getSummary(FunctionDecl* FD);
|
2008-06-26 01:21:56 +04:00
|
|
|
RetainSummary* getMethodSummary(ObjCMessageExpr* ME, ObjCInterfaceDecl* ID);
|
2008-06-24 02:21:20 +04:00
|
|
|
RetainSummary* getClassMethodSummary(IdentifierInfo* ClsName, Selector S);
|
2008-05-06 08:20:12 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
bool isGCEnabled() const { return GCEnabled; }
|
2008-03-11 09:39:11 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Implementation of checker data structures.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummaryManager::~RetainSummaryManager() {
|
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-05-06 03:55:01 +04:00
|
|
|
for (ArgEffectsSetTy::iterator I = ArgEffectsSet.begin(),
|
|
|
|
E = ArgEffectsSet.end(); I!=E; ++I)
|
2008-03-11 09:39:11 +03:00
|
|
|
I->getValue().~ArgEffects();
|
2008-03-06 03:08:09 +03:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
ArgEffects* RetainSummaryManager::getArgEffects() {
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
if (ScratchArgs.empty())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Compute a profile for a non-empty ScratchArgs.
|
2008-03-12 04:21:45 +03:00
|
|
|
llvm::FoldingSetNodeID profile;
|
|
|
|
profile.Add(ScratchArgs);
|
|
|
|
void* InsertPos;
|
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
// Look up the uniqued copy, or create a new one.
|
2008-03-12 04:21:45 +03:00
|
|
|
llvm::FoldingSetNodeWrapper<ArgEffects>* E =
|
2008-05-06 02:11:16 +04:00
|
|
|
ArgEffectsSet.FindNodeOrInsertPos(profile, InsertPos);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
if (E) {
|
2008-03-12 04:21:45 +03:00
|
|
|
ScratchArgs.clear();
|
|
|
|
return &E->getValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
E = (llvm::FoldingSetNodeWrapper<ArgEffects>*)
|
2008-06-26 01:21:56 +04:00
|
|
|
BPAlloc.Allocate<llvm::FoldingSetNodeWrapper<ArgEffects> >();
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
new (E) llvm::FoldingSetNodeWrapper<ArgEffects>(ScratchArgs);
|
2008-05-06 02:11:16 +04:00
|
|
|
ArgEffectsSet.InsertNode(E, InsertPos);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
ScratchArgs.clear();
|
|
|
|
return &E->getValue();
|
|
|
|
}
|
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
RetainSummary*
|
|
|
|
RetainSummaryManager::getPersistentSummary(ArgEffects* AE, RetEffect RetEff,
|
2008-05-06 19:44:25 +04:00
|
|
|
ArgEffect ReceiverEff,
|
2008-07-18 21:24:20 +04:00
|
|
|
ArgEffect DefaultEff,
|
|
|
|
bool isEndPath) {
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
// Generate a profile for the summary.
|
2008-03-12 04:21:45 +03:00
|
|
|
llvm::FoldingSetNodeID profile;
|
2008-07-18 21:39:56 +04:00
|
|
|
RetainSummary::Profile(profile, AE, RetEff, DefaultEff, ReceiverEff,
|
|
|
|
isEndPath);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
// Look up the uniqued summary, or create one if it doesn't exist.
|
|
|
|
void* InsertPos;
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary* Summ = SummarySet.FindNodeOrInsertPos(profile, InsertPos);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
if (Summ)
|
|
|
|
return Summ;
|
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
// Create the summary and return it.
|
2008-05-06 02:11:16 +04:00
|
|
|
Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
|
2008-07-18 21:24:20 +04:00
|
|
|
new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff, isEndPath);
|
2008-03-12 04:21:45 +03:00
|
|
|
SummarySet.InsertNode(Summ, InsertPos);
|
|
|
|
|
|
|
|
return Summ;
|
|
|
|
}
|
|
|
|
|
2009-01-07 03:39:56 +03:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Predicates.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
bool RetainSummaryManager::isTrackedObjectType(QualType T) {
|
|
|
|
if (!Ctx.isObjCObjectPointerType(T))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Does it subclass NSObject?
|
|
|
|
ObjCInterfaceType* OT = dyn_cast<ObjCInterfaceType>(T.getTypePtr());
|
|
|
|
|
|
|
|
// We assume that id<..>, id, and "Class" all represent tracked objects.
|
|
|
|
if (!OT)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Does the object type subclass NSObject?
|
|
|
|
// FIXME: We can memoize here if this gets too expensive.
|
|
|
|
IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject");
|
|
|
|
ObjCInterfaceDecl* ID = OT->getDecl();
|
|
|
|
|
|
|
|
for ( ; ID ; ID = ID->getSuperClass())
|
|
|
|
if (ID->getIdentifier() == NSObjectII)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Summary creation for functions (largely uses of Core Foundation).
|
|
|
|
//===----------------------------------------------------------------------===//
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2009-01-13 00:45:02 +03:00
|
|
|
static bool isRetain(FunctionDecl* FD, const char* FName) {
|
|
|
|
const char* loc = strstr(FName, "Retain");
|
|
|
|
return loc && loc[sizeof("Retain")-1] == '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isRelease(FunctionDecl* FD, const char* FName) {
|
|
|
|
const char* loc = strstr(FName, "Release");
|
|
|
|
return loc && loc[sizeof("Release")-1] == '\0';
|
|
|
|
}
|
|
|
|
|
2008-06-24 07:56:45 +04:00
|
|
|
RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) {
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
SourceLocation Loc = FD->getLocation();
|
|
|
|
|
|
|
|
if (!Loc.isFileID())
|
|
|
|
return NULL;
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-04-24 21:22:33 +04:00
|
|
|
// Look up a summary in our cache of FunctionDecls -> Summaries.
|
2008-05-06 02:11:16 +04:00
|
|
|
FuncSummariesTy::iterator I = FuncSummaries.find(FD);
|
2008-04-24 21:22:33 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
if (I != FuncSummaries.end())
|
2008-04-24 21:22:33 +04:00
|
|
|
return I->second;
|
|
|
|
|
|
|
|
// No summary. Generate one.
|
2009-01-13 00:45:02 +03:00
|
|
|
RetainSummary *S = 0;
|
2008-05-05 20:51:50 +04:00
|
|
|
|
2008-07-15 20:50:12 +04:00
|
|
|
do {
|
2009-01-13 00:45:02 +03:00
|
|
|
// We generate "stop" summaries for implicitly defined functions.
|
|
|
|
if (FD->isImplicit()) {
|
|
|
|
S = getPersistentStopSummary();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-02-23 05:42:56 +03:00
|
|
|
// [PR 3337] Use 'getDesugaredType' to strip away any typedefs on the
|
2009-01-16 21:40:33 +03:00
|
|
|
// function's type.
|
|
|
|
FunctionType* FT = cast<FunctionType>(FD->getType()->getDesugaredType());
|
2009-01-13 00:45:02 +03:00
|
|
|
const char* FName = FD->getIdentifier()->getName();
|
|
|
|
|
|
|
|
// Inspect the result type.
|
|
|
|
QualType RetTy = FT->getResultType();
|
|
|
|
|
|
|
|
// FIXME: This should all be refactored into a chain of "summary lookup"
|
|
|
|
// filters.
|
|
|
|
if (strcmp(FName, "IOServiceGetMatchingServices") == 0) {
|
|
|
|
// FIXES: <rdar://problem/6326900>
|
|
|
|
// This should be addressed using a API table. This strcmp is also
|
|
|
|
// a little gross, but there is no need to super optimize here.
|
|
|
|
assert (ScratchArgs.empty());
|
|
|
|
ScratchArgs.push_back(std::make_pair(1, DecRef));
|
|
|
|
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle: id NSMakeCollectable(CFTypeRef)
|
|
|
|
if (strcmp(FName, "NSMakeCollectable") == 0) {
|
|
|
|
S = (RetTy == Ctx.getObjCIdType())
|
|
|
|
? getUnarySummary(FT, cfmakecollectable)
|
|
|
|
: getPersistentStopSummary();
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (RetTy->isPointerType()) {
|
|
|
|
// For CoreFoundation ('CF') types.
|
|
|
|
if (isRefType(RetTy, "CF", &Ctx, FName)) {
|
|
|
|
if (isRetain(FD, FName))
|
|
|
|
S = getUnarySummary(FT, cfretain);
|
|
|
|
else if (strstr(FName, "MakeCollectable"))
|
|
|
|
S = getUnarySummary(FT, cfmakecollectable);
|
|
|
|
else
|
|
|
|
S = getCFCreateGetRuleSummary(FD, FName);
|
|
|
|
|
2008-07-15 20:50:12 +04:00
|
|
|
break;
|
|
|
|
}
|
2009-01-13 00:45:02 +03:00
|
|
|
|
|
|
|
// For CoreGraphics ('CG') types.
|
|
|
|
if (isRefType(RetTy, "CG", &Ctx, FName)) {
|
|
|
|
if (isRetain(FD, FName))
|
|
|
|
S = getUnarySummary(FT, cfretain);
|
|
|
|
else
|
|
|
|
S = getCFCreateGetRuleSummary(FD, FName);
|
|
|
|
|
2008-07-15 20:50:12 +04:00
|
|
|
break;
|
|
|
|
}
|
2009-01-13 00:45:02 +03:00
|
|
|
|
|
|
|
// For the Disk Arbitration API (DiskArbitration/DADisk.h)
|
|
|
|
if (isRefType(RetTy, "DADisk") ||
|
|
|
|
isRefType(RetTy, "DADissenter") ||
|
|
|
|
isRefType(RetTy, "DASessionRef")) {
|
|
|
|
S = getCFCreateGetRuleSummary(FD, FName);
|
2008-10-29 07:07:07 +03:00
|
|
|
break;
|
|
|
|
}
|
2009-01-13 00:45:02 +03:00
|
|
|
|
|
|
|
break;
|
2008-07-15 20:50:12 +04:00
|
|
|
}
|
2009-01-13 00:45:02 +03:00
|
|
|
|
|
|
|
// Check for release functions, the only kind of functions that we care
|
|
|
|
// about that don't return a pointer type.
|
|
|
|
if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G')) {
|
|
|
|
if (isRelease(FD, FName+2))
|
|
|
|
S = getUnarySummary(FT, cfrelease);
|
|
|
|
else {
|
2009-01-30 01:45:13 +03:00
|
|
|
assert (ScratchArgs.empty());
|
|
|
|
// Remaining CoreFoundation and CoreGraphics functions.
|
|
|
|
// We use to assume that they all strictly followed the ownership idiom
|
|
|
|
// and that ownership cannot be transferred. While this is technically
|
|
|
|
// correct, many methods allow a tracked object to escape. For example:
|
|
|
|
//
|
|
|
|
// CFMutableDictionaryRef x = CFDictionaryCreateMutable(...);
|
|
|
|
// CFDictionaryAddValue(y, key, x);
|
|
|
|
// CFRelease(x);
|
|
|
|
// ... it is okay to use 'x' since 'y' has a reference to it
|
|
|
|
//
|
|
|
|
// We handle this and similar cases with the follow heuristic. If the
|
|
|
|
// function name contains "InsertValue", "SetValue" or "AddValue" then
|
|
|
|
// we assume that arguments may "escape."
|
|
|
|
//
|
|
|
|
ArgEffect E = (CStrInCStrNoCase(FName, "InsertValue") ||
|
|
|
|
CStrInCStrNoCase(FName, "AddValue") ||
|
2009-02-06 01:34:53 +03:00
|
|
|
CStrInCStrNoCase(FName, "SetValue") ||
|
|
|
|
CStrInCStrNoCase(FName, "AppendValue"))
|
2009-01-30 01:45:13 +03:00
|
|
|
? MayEscape : DoNothing;
|
|
|
|
|
|
|
|
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, E);
|
2009-01-13 00:45:02 +03:00
|
|
|
}
|
2008-10-23 00:54:52 +04:00
|
|
|
}
|
2008-07-15 20:50:12 +04:00
|
|
|
}
|
|
|
|
while (0);
|
2008-04-24 21:22:33 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
FuncSummaries[FD] = S;
|
2008-05-05 20:51:50 +04:00
|
|
|
return S;
|
2008-03-06 03:08:09 +03:00
|
|
|
}
|
|
|
|
|
2008-07-15 20:50:12 +04:00
|
|
|
RetainSummary*
|
|
|
|
RetainSummaryManager::getCFCreateGetRuleSummary(FunctionDecl* FD,
|
|
|
|
const char* FName) {
|
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
if (strstr(FName, "Create") || strstr(FName, "Copy"))
|
|
|
|
return getCFSummaryCreateRule(FD);
|
2008-07-15 20:50:12 +04:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
if (strstr(FName, "Get"))
|
2008-05-05 20:51:50 +04:00
|
|
|
return getCFSummaryGetRule(FD);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
return 0;
|
2008-03-12 04:21:45 +03:00
|
|
|
}
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary*
|
2009-01-13 00:45:02 +03:00
|
|
|
RetainSummaryManager::getUnarySummary(FunctionType* FT, UnaryFuncKind func) {
|
|
|
|
// Sanity check that this is *really* a unary function. This can
|
|
|
|
// happen if people do weird things.
|
|
|
|
FunctionTypeProto* FTP = dyn_cast<FunctionTypeProto>(FT);
|
|
|
|
if (!FTP || FTP->getNumArgs() != 1)
|
|
|
|
return getPersistentStopSummary();
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-05-05 20:51:50 +04:00
|
|
|
assert (ScratchArgs.empty());
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-04-29 09:33:51 +04:00
|
|
|
switch (func) {
|
2009-01-13 00:45:02 +03:00
|
|
|
case cfretain: {
|
2008-04-29 09:33:51 +04:00
|
|
|
ScratchArgs.push_back(std::make_pair(0, IncRef));
|
2008-05-22 21:31:13 +04:00
|
|
|
return getPersistentSummary(RetEffect::MakeAlias(0),
|
|
|
|
DoNothing, DoNothing);
|
2008-04-29 09:33:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
case cfrelease: {
|
|
|
|
ScratchArgs.push_back(std::make_pair(0, DecRef));
|
2008-05-22 21:31:13 +04:00
|
|
|
return getPersistentSummary(RetEffect::MakeNoRet(),
|
|
|
|
DoNothing, DoNothing);
|
2008-04-29 09:33:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
case cfmakecollectable: {
|
2009-02-19 00:57:45 +03:00
|
|
|
ScratchArgs.push_back(std::make_pair(0, MakeCollectable));
|
|
|
|
return getPersistentSummary(RetEffect::MakeAlias(0),DoNothing, DoNothing);
|
2008-04-29 09:33:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2008-05-05 20:51:50 +04:00
|
|
|
assert (false && "Not a supported unary function.");
|
2008-08-13 00:41:56 +04:00
|
|
|
return 0;
|
2008-04-11 03:44:06 +04:00
|
|
|
}
|
2008-03-12 04:21:45 +03:00
|
|
|
}
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) {
|
2008-04-24 21:22:33 +04:00
|
|
|
assert (ScratchArgs.empty());
|
2008-07-09 22:11:16 +04:00
|
|
|
|
|
|
|
if (FD->getIdentifier() == CFDictionaryCreateII) {
|
|
|
|
ScratchArgs.push_back(std::make_pair(1, DoNothingByRef));
|
|
|
|
ScratchArgs.push_back(std::make_pair(2, DoNothingByRef));
|
|
|
|
}
|
|
|
|
|
2009-01-28 08:56:51 +03:00
|
|
|
return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true));
|
2008-03-12 04:21:45 +03:00
|
|
|
}
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummary* RetainSummaryManager::getCFSummaryGetRule(FunctionDecl* FD) {
|
2008-04-24 21:22:33 +04:00
|
|
|
assert (ScratchArgs.empty());
|
2009-01-28 08:56:51 +03:00
|
|
|
return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF),
|
|
|
|
DoNothing, DoNothing);
|
2008-03-12 04:21:45 +03:00
|
|
|
}
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Summary creation for Selectors.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
RetainSummary*
|
2008-06-26 01:21:56 +04:00
|
|
|
RetainSummaryManager::getInitMethodSummary(ObjCMessageExpr* ME) {
|
2008-05-06 03:55:01 +04:00
|
|
|
assert(ScratchArgs.empty());
|
|
|
|
|
2009-02-20 03:05:35 +03:00
|
|
|
// 'init' methods only return an alias if the return type is a location type.
|
|
|
|
QualType T = ME->getType();
|
2008-05-06 03:55:01 +04:00
|
|
|
RetainSummary* Summ =
|
2009-02-20 03:05:35 +03:00
|
|
|
getPersistentSummary(Loc::IsLocType(T) ? RetEffect::MakeReceiverAlias()
|
|
|
|
: RetEffect::MakeNoRet());
|
2008-05-06 03:55:01 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
ObjCMethodSummaries[ME] = Summ;
|
2008-05-06 03:55:01 +04:00
|
|
|
return Summ;
|
|
|
|
}
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
|
2008-05-06 19:44:25 +04:00
|
|
|
RetainSummary*
|
2008-06-26 01:21:56 +04:00
|
|
|
RetainSummaryManager::getMethodSummary(ObjCMessageExpr* ME,
|
|
|
|
ObjCInterfaceDecl* ID) {
|
2008-05-06 19:44:25 +04:00
|
|
|
|
|
|
|
Selector S = ME->getSelector();
|
2008-05-06 03:55:01 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
// Look up a summary in our summary cache.
|
|
|
|
ObjCMethodSummariesTy::iterator I = ObjCMethodSummaries.find(ID, S);
|
2008-05-06 03:55:01 +04:00
|
|
|
|
2008-06-24 02:21:20 +04:00
|
|
|
if (I != ObjCMethodSummaries.end())
|
2008-05-06 03:55:01 +04:00
|
|
|
return I->second;
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2009-01-07 03:39:56 +03:00
|
|
|
// "initXXX": pass-through for receiver.
|
2008-05-06 03:55:01 +04:00
|
|
|
const char* s = S.getIdentifierInfoForSlot(0)->getName();
|
2008-05-07 07:45:05 +04:00
|
|
|
assert (ScratchArgs.empty());
|
2008-05-06 10:09:09 +04:00
|
|
|
|
2009-02-21 08:13:43 +03:00
|
|
|
if (deriveNamingConvention(s) == InitRule)
|
2009-01-07 03:39:56 +03:00
|
|
|
return getInitMethodSummary(ME);
|
2008-05-06 19:44:25 +04:00
|
|
|
|
2009-01-07 03:39:56 +03:00
|
|
|
// Look for methods that return an owned object.
|
|
|
|
if (!isTrackedObjectType(Ctx.getCanonicalType(ME->getType())))
|
2008-05-07 08:25:59 +04:00
|
|
|
return 0;
|
2008-05-06 19:44:25 +04:00
|
|
|
|
2009-01-07 03:39:56 +03:00
|
|
|
if (followsFundamentalRule(s)) {
|
|
|
|
RetEffect E = isGCEnabled() ? RetEffect::MakeNoRet()
|
2009-01-28 08:56:51 +03:00
|
|
|
: RetEffect::MakeOwned(RetEffect::ObjC, true);
|
2008-05-07 07:45:05 +04:00
|
|
|
RetainSummary* Summ = getPersistentSummary(E);
|
2008-06-26 01:21:56 +04:00
|
|
|
ObjCMethodSummaries[ME] = Summ;
|
2008-05-07 07:45:05 +04:00
|
|
|
return Summ;
|
2008-05-06 19:44:25 +04:00
|
|
|
}
|
|
|
|
|
2008-05-06 03:55:01 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-05-07 01:26:51 +04:00
|
|
|
RetainSummary*
|
2008-06-24 02:21:20 +04:00
|
|
|
RetainSummaryManager::getClassMethodSummary(IdentifierInfo* ClsName,
|
|
|
|
Selector S) {
|
2008-05-07 01:26:51 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
// FIXME: Eventually we should properly do class method summaries, but
|
|
|
|
// it requires us being able to walk the type hierarchy. Unfortunately,
|
|
|
|
// we cannot do this with just an IdentifierInfo* for the class name.
|
|
|
|
|
2008-05-07 01:26:51 +04:00
|
|
|
// Look up a summary in our cache of Selectors -> Summaries.
|
2008-06-26 01:21:56 +04:00
|
|
|
ObjCMethodSummariesTy::iterator I = ObjCClassMethodSummaries.find(ClsName, S);
|
2008-05-07 01:26:51 +04:00
|
|
|
|
2008-06-24 02:21:20 +04:00
|
|
|
if (I != ObjCClassMethodSummaries.end())
|
2008-05-07 01:26:51 +04:00
|
|
|
return I->second;
|
|
|
|
|
2008-05-07 03:07:13 +04:00
|
|
|
return 0;
|
2008-05-07 01:26:51 +04:00
|
|
|
}
|
|
|
|
|
2008-06-24 02:21:20 +04:00
|
|
|
void RetainSummaryManager::InitializeClassMethodSummaries() {
|
2008-05-06 04:30:21 +04:00
|
|
|
|
|
|
|
assert (ScratchArgs.empty());
|
|
|
|
|
2008-06-23 22:02:52 +04:00
|
|
|
RetEffect E = isGCEnabled() ? RetEffect::MakeNoRet()
|
2009-01-28 08:56:51 +03:00
|
|
|
: RetEffect::MakeOwned(RetEffect::ObjC, true);
|
2008-06-23 22:02:52 +04:00
|
|
|
|
2008-05-06 04:30:21 +04:00
|
|
|
RetainSummary* Summ = getPersistentSummary(E);
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
// Create the summaries for "alloc", "new", and "allocWithZone:" for
|
|
|
|
// NSObject and its derivatives.
|
|
|
|
addNSObjectClsMethSummary(GetNullarySelector("alloc", Ctx), Summ);
|
|
|
|
addNSObjectClsMethSummary(GetNullarySelector("new", Ctx), Summ);
|
|
|
|
addNSObjectClsMethSummary(GetUnarySelector("allocWithZone", Ctx), Summ);
|
2008-07-18 21:24:20 +04:00
|
|
|
|
|
|
|
// Create the [NSAssertionHandler currentHander] summary.
|
2008-08-12 22:30:56 +04:00
|
|
|
addClsMethSummary(&Ctx.Idents.get("NSAssertionHandler"),
|
2009-01-28 08:56:51 +03:00
|
|
|
GetNullarySelector("currentHandler", Ctx),
|
|
|
|
getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC)));
|
2008-10-21 19:53:15 +04:00
|
|
|
|
|
|
|
// Create the [NSAutoreleasePool addObject:] summary.
|
2009-01-29 00:44:40 +03:00
|
|
|
ScratchArgs.push_back(std::make_pair(0, Autorelease));
|
|
|
|
addClsMethSummary(&Ctx.Idents.get("NSAutoreleasePool"),
|
|
|
|
GetUnarySelector("addObject", Ctx),
|
|
|
|
getPersistentSummary(RetEffect::MakeNoRet(),
|
2009-02-23 05:31:16 +03:00
|
|
|
DoNothing, Autorelease));
|
2008-05-06 04:30:21 +04:00
|
|
|
}
|
|
|
|
|
2008-06-24 02:21:20 +04:00
|
|
|
void RetainSummaryManager::InitializeMethodSummaries() {
|
2008-05-06 04:38:54 +04:00
|
|
|
|
|
|
|
assert (ScratchArgs.empty());
|
|
|
|
|
2008-05-07 01:26:51 +04:00
|
|
|
// Create the "init" selector. It just acts as a pass-through for the
|
|
|
|
// receiver.
|
2008-07-01 21:21:27 +04:00
|
|
|
RetainSummary* InitSumm = getPersistentSummary(RetEffect::MakeReceiverAlias());
|
|
|
|
addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm);
|
2008-05-07 01:26:51 +04:00
|
|
|
|
|
|
|
// The next methods are allocators.
|
2008-06-23 22:02:52 +04:00
|
|
|
RetEffect E = isGCEnabled() ? RetEffect::MakeNoRet()
|
2009-01-28 08:56:51 +03:00
|
|
|
: RetEffect::MakeOwned(RetEffect::ObjC, true);
|
2008-06-23 22:02:52 +04:00
|
|
|
|
2008-07-01 21:21:27 +04:00
|
|
|
RetainSummary* Summ = getPersistentSummary(E);
|
2008-05-07 01:26:51 +04:00
|
|
|
|
|
|
|
// Create the "copy" selector.
|
2008-08-13 00:41:56 +04:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("copy", Ctx), Summ);
|
|
|
|
|
2008-05-06 04:38:54 +04:00
|
|
|
// Create the "mutableCopy" selector.
|
2008-06-26 01:21:56 +04:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("mutableCopy", Ctx), Summ);
|
2008-08-13 00:41:56 +04:00
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
// Create the "retain" selector.
|
|
|
|
E = RetEffect::MakeReceiverAlias();
|
2009-02-18 21:54:33 +03:00
|
|
|
Summ = getPersistentSummary(E, IncRefMsg);
|
2008-06-26 01:21:56 +04:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ);
|
2008-05-06 06:26:56 +04:00
|
|
|
|
|
|
|
// Create the "release" selector.
|
2009-02-18 21:54:33 +03:00
|
|
|
Summ = getPersistentSummary(E, DecRefMsg);
|
2008-06-26 01:21:56 +04:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ);
|
2008-05-08 01:17:39 +04:00
|
|
|
|
|
|
|
// Create the "drain" selector.
|
|
|
|
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef);
|
2008-06-26 01:21:56 +04:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ);
|
2008-05-06 06:26:56 +04:00
|
|
|
|
|
|
|
// Create the "autorelease" selector.
|
2009-01-29 00:44:40 +03:00
|
|
|
Summ = getPersistentSummary(E, Autorelease);
|
2008-06-26 01:21:56 +04:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ);
|
2008-08-13 00:41:56 +04:00
|
|
|
|
2008-08-12 22:48:50 +04:00
|
|
|
// For NSWindow, allocated objects are (initially) self-owned.
|
2009-02-23 05:51:29 +03:00
|
|
|
// FIXME: For now we opt for false negatives with NSWindow, as these objects
|
|
|
|
// self-own themselves. However, they only do this once they are displayed.
|
|
|
|
// Thus, we need to track an NSWindow's display status.
|
|
|
|
// This is tracked in <rdar://problem/6062711>.
|
2008-08-12 22:48:50 +04:00
|
|
|
RetainSummary *NSWindowSumm =
|
2009-02-23 05:51:29 +03:00
|
|
|
getPersistentSummary(RetEffect::MakeReceiverAlias(), StopTracking);
|
2008-08-12 22:48:50 +04:00
|
|
|
|
|
|
|
addInstMethSummary("NSWindow", NSWindowSumm, "initWithContentRect",
|
|
|
|
"styleMask", "backing", "defer", NULL);
|
|
|
|
|
|
|
|
addInstMethSummary("NSWindow", NSWindowSumm, "initWithContentRect",
|
|
|
|
"styleMask", "backing", "defer", "screen", NULL);
|
|
|
|
|
2008-07-01 21:21:27 +04:00
|
|
|
// For NSPanel (which subclasses NSWindow), allocated objects are not
|
|
|
|
// self-owned.
|
2008-08-12 22:48:50 +04:00
|
|
|
addInstMethSummary("NSPanel", InitSumm, "initWithContentRect",
|
|
|
|
"styleMask", "backing", "defer", NULL);
|
2008-07-01 21:21:27 +04:00
|
|
|
|
2008-08-12 22:48:50 +04:00
|
|
|
addInstMethSummary("NSPanel", InitSumm, "initWithContentRect",
|
|
|
|
"styleMask", "backing", "defer", "screen", NULL);
|
2008-06-26 01:21:56 +04:00
|
|
|
|
2008-07-18 21:24:20 +04:00
|
|
|
// Create NSAssertionHandler summaries.
|
2008-08-12 22:30:56 +04:00
|
|
|
addPanicSummary("NSAssertionHandler", "handleFailureInFunction", "file",
|
|
|
|
"lineNumber", "description", NULL);
|
|
|
|
|
|
|
|
addPanicSummary("NSAssertionHandler", "handleFailureInMethod", "object",
|
|
|
|
"file", "lineNumber", "description", NULL);
|
2008-05-06 04:38:54 +04:00
|
|
|
}
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-04-17 00:40:59 +04:00
|
|
|
// Reference-counting logic (typestate + counts).
|
2008-03-11 09:39:11 +03:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
class VISIBILITY_HIDDEN RefVal {
|
2008-04-17 22:12:53 +04:00
|
|
|
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.
|
2008-10-23 03:56:21 +04:00
|
|
|
ErrorLeak, // A memory leak due to excessive reference counts.
|
|
|
|
ErrorLeakReturned // A memory leak due to the returning method not having
|
|
|
|
// the correct naming conventions.
|
2008-04-17 22:12:53 +04:00
|
|
|
};
|
2009-01-28 08:56:51 +03:00
|
|
|
|
|
|
|
private:
|
2008-04-17 22:12:53 +04:00
|
|
|
Kind kind;
|
2009-01-28 08:56:51 +03:00
|
|
|
RetEffect::ObjKind okind;
|
2008-04-17 22:12:53 +04:00
|
|
|
unsigned Cnt;
|
2008-06-26 01:21:56 +04:00
|
|
|
QualType T;
|
|
|
|
|
2009-01-28 08:56:51 +03:00
|
|
|
RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, QualType t)
|
|
|
|
: kind(k), okind(o), Cnt(cnt), T(t) {}
|
2008-03-11 20:48:22 +03:00
|
|
|
|
2009-01-28 08:56:51 +03:00
|
|
|
RefVal(Kind k, unsigned cnt = 0)
|
|
|
|
: kind(k), okind(RetEffect::AnyObj), Cnt(cnt) {}
|
|
|
|
|
|
|
|
public:
|
2008-04-17 22:12:53 +04:00
|
|
|
Kind getKind() const { return kind; }
|
2009-01-28 08:56:51 +03:00
|
|
|
|
|
|
|
RetEffect::ObjKind getObjKind() const { return okind; }
|
2008-03-11 20:48:22 +03:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
unsigned getCount() const { return Cnt; }
|
|
|
|
QualType getType() const { return T; }
|
2008-04-17 22:12:53 +04:00
|
|
|
|
|
|
|
// Useful predicates.
|
2008-03-11 20:48:22 +03:00
|
|
|
|
2008-03-11 21:14:09 +03:00
|
|
|
static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
|
|
|
|
|
2008-10-25 00:32:50 +04:00
|
|
|
static bool isLeak(Kind k) { return k >= ErrorLeak; }
|
2008-04-17 02:32:20 +04:00
|
|
|
|
2008-04-12 02:25:11 +04:00
|
|
|
bool isOwned() const {
|
|
|
|
return getKind() == Owned;
|
|
|
|
}
|
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
bool isNotOwned() const {
|
|
|
|
return getKind() == NotOwned;
|
|
|
|
}
|
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
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.
|
|
|
|
|
2009-01-28 08:56:51 +03:00
|
|
|
static RefVal makeOwned(RetEffect::ObjKind o, QualType t,
|
|
|
|
unsigned Count = 1) {
|
|
|
|
return RefVal(Owned, o, Count, t);
|
2008-04-11 03:09:18 +04:00
|
|
|
}
|
|
|
|
|
2009-01-28 08:56:51 +03:00
|
|
|
static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t,
|
|
|
|
unsigned Count = 0) {
|
|
|
|
return RefVal(NotOwned, o, Count, t);
|
2008-04-11 03:09:18 +04:00
|
|
|
}
|
2008-04-17 22:12:53 +04:00
|
|
|
|
|
|
|
static RefVal makeReturnedOwned(unsigned Count) {
|
|
|
|
return RefVal(ReturnedOwned, Count);
|
|
|
|
}
|
|
|
|
|
|
|
|
static RefVal makeReturnedNotOwned() {
|
|
|
|
return RefVal(ReturnedNotOwned);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Comparison, profiling, and pretty-printing.
|
2008-03-11 20:48:22 +03:00
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
bool operator==(const RefVal& X) const {
|
2008-06-26 01:21:56 +04:00
|
|
|
return kind == X.kind && Cnt == X.Cnt && T == X.T;
|
2008-04-17 22:12:53 +04:00
|
|
|
}
|
2008-03-11 22:44:10 +03:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
RefVal operator-(size_t i) const {
|
2009-01-28 08:56:51 +03:00
|
|
|
return RefVal(getKind(), getObjKind(), getCount() - i, getType());
|
2008-06-26 01:21:56 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
RefVal operator+(size_t i) const {
|
2009-01-28 08:56:51 +03:00
|
|
|
return RefVal(getKind(), getObjKind(), getCount() + i, getType());
|
2008-06-26 01:21:56 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
RefVal operator^(Kind k) const {
|
2009-01-28 08:56:51 +03:00
|
|
|
return RefVal(k, getObjKind(), getCount(), getType());
|
2008-06-26 01:21:56 +04:00
|
|
|
}
|
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
void Profile(llvm::FoldingSetNodeID& ID) const {
|
|
|
|
ID.AddInteger((unsigned) kind);
|
|
|
|
ID.AddInteger(Cnt);
|
2008-06-26 01:21:56 +04:00
|
|
|
ID.Add(T);
|
2008-04-17 22:12:53 +04:00
|
|
|
}
|
|
|
|
|
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 {
|
2008-06-26 01:21:56 +04:00
|
|
|
if (!T.isNull())
|
|
|
|
Out << "Tracked Type:" << T.getAsString() << '\n';
|
|
|
|
|
2008-03-11 22:44:10 +03:00
|
|
|
switch (getKind()) {
|
|
|
|
default: assert(false);
|
2008-04-11 03:09:18 +04:00
|
|
|
case Owned: {
|
|
|
|
Out << "Owned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
2008-03-11 22:44:10 +03:00
|
|
|
break;
|
2008-04-11 03:09:18 +04:00
|
|
|
}
|
2008-03-11 22:44:10 +03:00
|
|
|
|
2008-04-11 03:09:18 +04:00
|
|
|
case NotOwned: {
|
2008-04-17 22:12:53 +04:00
|
|
|
Out << "NotOwned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ReturnedOwned: {
|
|
|
|
Out << "ReturnedOwned";
|
2008-04-11 03:09:18 +04:00
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
2008-03-11 22:44:10 +03:00
|
|
|
break;
|
2008-04-11 03:09:18 +04:00
|
|
|
}
|
2008-03-11 22:44:10 +03:00
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
case ReturnedNotOwned: {
|
|
|
|
Out << "ReturnedNotOwned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-03-11 22:44:10 +03:00
|
|
|
case Released:
|
|
|
|
Out << "Released";
|
|
|
|
break;
|
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
case ErrorLeak:
|
|
|
|
Out << "Leaked";
|
|
|
|
break;
|
|
|
|
|
2008-10-23 03:56:21 +04:00
|
|
|
case ErrorLeakReturned:
|
|
|
|
Out << "Leaked (Bad naming)";
|
|
|
|
break;
|
|
|
|
|
2008-03-11 22:44:10 +03:00
|
|
|
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-08-15 01:16:54 +04:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// RefBindings - State used to track object reference counts.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-12-05 05:27:51 +03:00
|
|
|
typedef llvm::ImmutableMap<SymbolRef, RefVal> RefBindings;
|
2008-08-15 01:16:54 +04:00
|
|
|
static int RefBIndex = 0;
|
2009-02-20 02:47:02 +03:00
|
|
|
static std::pair<const void*, const void*> LeakProgramPointTag(&RefBIndex, 0);
|
2008-08-15 01:16:54 +04:00
|
|
|
|
|
|
|
namespace clang {
|
2008-08-17 07:20:02 +04:00
|
|
|
template<>
|
|
|
|
struct GRStateTrait<RefBindings> : public GRStatePartialTrait<RefBindings> {
|
|
|
|
static inline void* GDMIndex() { return &RefBIndex; }
|
|
|
|
};
|
|
|
|
}
|
2008-10-21 19:53:15 +04:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// ARBindings - State used to track objects in autorelease pools.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-12-05 05:27:51 +03:00
|
|
|
typedef llvm::ImmutableSet<SymbolRef> ARPoolContents;
|
|
|
|
typedef llvm::ImmutableList< std::pair<SymbolRef, ARPoolContents*> > ARBindings;
|
2008-10-21 19:53:15 +04:00
|
|
|
static int AutoRBIndex = 0;
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
template<>
|
|
|
|
struct GRStateTrait<ARBindings> : public GRStatePartialTrait<ARBindings> {
|
|
|
|
static inline void* GDMIndex() { return &AutoRBIndex; }
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2008-04-17 00:40:59 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Transfer functions.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-08-15 01:16:54 +04:00
|
|
|
namespace {
|
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
|
2008-04-18 07:39:05 +04:00
|
|
|
public:
|
2008-08-14 01:24:49 +04:00
|
|
|
class BindingsPrinter : public GRState::Printer {
|
2008-03-11 22:44:10 +03:00
|
|
|
public:
|
2008-08-14 01:24:49 +04:00
|
|
|
virtual void Print(std::ostream& Out, const GRState* state,
|
|
|
|
const char* nl, const char* sep);
|
2008-03-11 22:44:10 +03:00
|
|
|
};
|
2008-04-18 07:39:05 +04:00
|
|
|
|
|
|
|
private:
|
2009-02-18 06:48:14 +03:00
|
|
|
typedef llvm::DenseMap<const GRExprEngine::NodeTy*, const RetainSummary*>
|
|
|
|
SummaryLogTy;
|
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
RetainSummaryManager Summaries;
|
2009-02-18 06:48:14 +03:00
|
|
|
SummaryLogTy SummaryLog;
|
2008-05-06 02:11:16 +04:00
|
|
|
const LangOptions& LOpts;
|
2008-08-17 07:20:02 +04:00
|
|
|
|
2009-02-05 09:50:21 +03:00
|
|
|
BugType *useAfterRelease, *releaseNotOwned;
|
|
|
|
BugType *leakWithinFunction, *leakAtReturn;
|
|
|
|
BugReporter *BR;
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-12-05 05:27:51 +03:00
|
|
|
RefBindings Update(RefBindings B, SymbolRef sym, RefVal V, ArgEffect E,
|
2008-08-17 07:20:02 +04:00
|
|
|
RefVal::Kind& hasErr, RefBindings::Factory& RefBFactory);
|
2008-04-16 08:28:53 +04:00
|
|
|
|
2008-12-05 05:27:51 +03:00
|
|
|
RefVal::Kind& Update(GRStateRef& state, SymbolRef sym, RefVal V,
|
2008-08-15 01:16:54 +04:00
|
|
|
ArgEffect E, RefVal::Kind& hasErr) {
|
|
|
|
|
|
|
|
state = state.set<RefBindings>(Update(state.get<RefBindings>(), sym, V,
|
2008-08-17 07:20:02 +04:00
|
|
|
E, hasErr,
|
|
|
|
state.get_context<RefBindings>()));
|
2008-08-15 01:16:54 +04:00
|
|
|
return hasErr;
|
|
|
|
}
|
|
|
|
|
2008-08-13 08:27:00 +04:00
|
|
|
void ProcessNonLeakError(ExplodedNodeSet<GRState>& Dst,
|
|
|
|
GRStmtNodeBuilder<GRState>& Builder,
|
2008-04-17 02:32:20 +04:00
|
|
|
Expr* NodeExpr, Expr* ErrorExpr,
|
2008-08-13 08:27:00 +04:00
|
|
|
ExplodedNode<GRState>* Pred,
|
|
|
|
const GRState* St,
|
2008-12-05 05:27:51 +03:00
|
|
|
RefVal::Kind hasErr, SymbolRef Sym);
|
2008-04-17 02:32:20 +04:00
|
|
|
|
2008-10-25 00:32:50 +04:00
|
|
|
std::pair<GRStateRef, bool>
|
|
|
|
HandleSymbolDeath(GRStateManager& VMgr, const GRState* St,
|
2008-12-05 05:27:51 +03:00
|
|
|
const Decl* CD, SymbolRef sid, RefVal V, bool& hasLeak);
|
2008-04-17 02:32:20 +04:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
public:
|
2008-04-17 00:40:59 +04:00
|
|
|
|
2008-07-22 20:21:24 +04:00
|
|
|
CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts)
|
2008-04-29 09:33:51 +04:00
|
|
|
: Summaries(Ctx, gcenabled),
|
2009-02-05 09:50:21 +03:00
|
|
|
LOpts(lopts), useAfterRelease(0), releaseNotOwned(0),
|
|
|
|
leakWithinFunction(0), leakAtReturn(0), BR(0) {}
|
2008-04-16 08:28:53 +04:00
|
|
|
|
2009-02-05 09:50:21 +03:00
|
|
|
virtual ~CFRefCount() {}
|
2008-04-10 03:49:11 +04:00
|
|
|
|
2009-02-05 02:49:09 +03:00
|
|
|
void RegisterChecks(BugReporter &BR);
|
2008-03-11 22:44:10 +03:00
|
|
|
|
2008-08-16 04:49:49 +04:00
|
|
|
virtual void RegisterPrinters(std::vector<GRState::Printer*>& Printers) {
|
|
|
|
Printers.push_back(new BindingsPrinter());
|
2008-03-11 22:44:10 +03:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
bool isGCEnabled() const { return Summaries.isGCEnabled(); }
|
2008-05-01 03:47:44 +04:00
|
|
|
const LangOptions& getLangOptions() const { return LOpts; }
|
|
|
|
|
2009-02-18 06:48:14 +03:00
|
|
|
const RetainSummary *getSummaryOfNode(const ExplodedNode<GRState> *N) const {
|
|
|
|
SummaryLogTy::const_iterator I = SummaryLog.find(N);
|
|
|
|
return I == SummaryLog.end() ? 0 : I->second;
|
|
|
|
}
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
// Calls.
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-08-13 08:27:00 +04:00
|
|
|
void EvalSummary(ExplodedNodeSet<GRState>& Dst,
|
2008-05-06 02:11:16 +04:00
|
|
|
GRExprEngine& Eng,
|
2008-08-13 08:27:00 +04:00
|
|
|
GRStmtNodeBuilder<GRState>& Builder,
|
2008-05-06 02:11:16 +04:00
|
|
|
Expr* Ex,
|
|
|
|
Expr* Receiver,
|
|
|
|
RetainSummary* Summ,
|
This patch is motivated by numerous strict-aliasing warnings when compiling
clang as a Release build.
The big change is that all AST nodes (subclasses of Stmt) whose children are
Expr* store their children as Stmt* or arrays of Stmt*. This is to remove
strict-aliasing warnings when using StmtIterator. None of the interfaces of any
of the classes have changed (except those with arg_iterators, see below), as the
accessor methods introduce the needed casts (via cast<>). While this extra
casting may seem cumbersome, it actually adds some important sanity checks
throughout the codebase, as clients using StmtIterator can potentially overwrite
children that are expected to be Expr* with Stmt* (that aren't Expr*). The casts
provide extra sanity checks that are operational in debug builds to catch
invariant violations such as these.
For classes that have arg_iterators (e.g., CallExpr), the definition of
arg_iterator has been replaced. Instead of it being Expr**, it is an actual
class (called ExprIterator) that wraps a Stmt**, and provides the necessary
operators for iteration. The nice thing about this class is that it also uses
cast<> to type-checking, which introduces extra sanity checks throughout the
codebase that are useful for debugging.
A few of the CodeGen functions that use arg_iterator (especially from
OverloadExpr) have been modified to take begin and end iterators instead of a
base Expr** and the number of arguments. This matches more with the abstraction
of iteration. This still needs to be cleaned up a little bit, as clients expect
that ExprIterator is a RandomAccessIterator (which we may or may not wish to
allow for efficiency of representation).
This is a fairly large patch. It passes the tests (except CodeGen/bitfield.c,
which was already broken) on both a Debug and Release build, but it should
obviously be reviewed.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@52378 91177308-0d34-0410-b5e6-96231b3b80d8
2008-06-17 06:43:46 +04:00
|
|
|
ExprIterator arg_beg, ExprIterator arg_end,
|
2008-08-13 08:27:00 +04:00
|
|
|
ExplodedNode<GRState>* Pred);
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-08-13 08:27:00 +04:00
|
|
|
virtual void EvalCall(ExplodedNodeSet<GRState>& Dst,
|
2008-03-13 00:06:49 +03:00
|
|
|
GRExprEngine& Eng,
|
2008-08-13 08:27:00 +04:00
|
|
|
GRStmtNodeBuilder<GRState>& Builder,
|
2008-10-17 09:57:07 +04:00
|
|
|
CallExpr* CE, SVal L,
|
2008-08-13 08:27:00 +04:00
|
|
|
ExplodedNode<GRState>* Pred);
|
2008-04-09 05:10:13 +04:00
|
|
|
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-08-13 08:27:00 +04:00
|
|
|
virtual void EvalObjCMessageExpr(ExplodedNodeSet<GRState>& Dst,
|
2008-04-16 03:44:31 +04:00
|
|
|
GRExprEngine& Engine,
|
2008-08-13 08:27:00 +04:00
|
|
|
GRStmtNodeBuilder<GRState>& Builder,
|
2008-04-16 03:44:31 +04:00
|
|
|
ObjCMessageExpr* ME,
|
2008-08-13 08:27:00 +04:00
|
|
|
ExplodedNode<GRState>* Pred);
|
2008-04-16 03:44:31 +04:00
|
|
|
|
2008-08-13 08:27:00 +04:00
|
|
|
bool EvalObjCMessageExprAux(ExplodedNodeSet<GRState>& Dst,
|
2008-04-16 03:44:31 +04:00
|
|
|
GRExprEngine& Engine,
|
2008-08-13 08:27:00 +04:00
|
|
|
GRStmtNodeBuilder<GRState>& Builder,
|
2008-04-16 03:44:31 +04:00
|
|
|
ObjCMessageExpr* ME,
|
2008-08-13 08:27:00 +04:00
|
|
|
ExplodedNode<GRState>* Pred);
|
2008-04-16 03:44:31 +04:00
|
|
|
|
2009-02-14 04:43:44 +03:00
|
|
|
// Stores.
|
|
|
|
virtual void EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val);
|
|
|
|
|
2008-04-12 02:25:11 +04:00
|
|
|
// End-of-path.
|
|
|
|
|
|
|
|
virtual void EvalEndPath(GRExprEngine& Engine,
|
2008-08-13 08:27:00 +04:00
|
|
|
GREndPathNodeBuilder<GRState>& Builder);
|
2008-04-12 02:25:11 +04:00
|
|
|
|
2008-08-13 08:27:00 +04:00
|
|
|
virtual void EvalDeadSymbols(ExplodedNodeSet<GRState>& Dst,
|
2008-04-25 03:57:27 +04:00
|
|
|
GRExprEngine& Engine,
|
2008-08-13 08:27:00 +04:00
|
|
|
GRStmtNodeBuilder<GRState>& Builder,
|
|
|
|
ExplodedNode<GRState>* Pred,
|
2009-01-22 01:26:05 +03:00
|
|
|
Stmt* S, const GRState* state,
|
|
|
|
SymbolReaper& SymReaper);
|
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
// Return statements.
|
|
|
|
|
2008-08-13 08:27:00 +04:00
|
|
|
virtual void EvalReturn(ExplodedNodeSet<GRState>& Dst,
|
2008-04-17 22:12:53 +04:00
|
|
|
GRExprEngine& Engine,
|
2008-08-13 08:27:00 +04:00
|
|
|
GRStmtNodeBuilder<GRState>& Builder,
|
2008-04-17 22:12:53 +04:00
|
|
|
ReturnStmt* S,
|
2008-08-13 08:27:00 +04:00
|
|
|
ExplodedNode<GRState>* Pred);
|
2008-04-18 23:23:43 +04:00
|
|
|
|
|
|
|
// Assumptions.
|
|
|
|
|
2008-08-13 08:27:00 +04:00
|
|
|
virtual const GRState* EvalAssume(GRStateManager& VMgr,
|
2008-10-17 09:57:07 +04:00
|
|
|
const GRState* St, SVal Cond,
|
2008-07-11 02:03:41 +04:00
|
|
|
bool Assumption, bool& isFeasible);
|
2008-03-11 09:39:11 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
|
2008-08-14 01:24:49 +04:00
|
|
|
void CFRefCount::BindingsPrinter::Print(std::ostream& Out, const GRState* state,
|
|
|
|
const char* nl, const char* sep) {
|
|
|
|
|
2008-08-15 01:16:54 +04:00
|
|
|
RefBindings B = state->get<RefBindings>();
|
2008-03-11 22:44:10 +03:00
|
|
|
|
2008-08-14 01:24:49 +04:00
|
|
|
if (!B.isEmpty())
|
2008-03-11 22:44:10 +03:00
|
|
|
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-05-06 02:11:16 +04:00
|
|
|
static inline ArgEffect GetArgE(RetainSummary* Summ, unsigned idx) {
|
2008-05-22 21:31:13 +04:00
|
|
|
return Summ ? Summ->getArg(idx) : MayEscape;
|
2008-04-12 00:23:24 +04:00
|
|
|
}
|
|
|
|
|
2008-05-06 06:26:56 +04:00
|
|
|
static inline RetEffect GetRetEffect(RetainSummary* Summ) {
|
|
|
|
return Summ ? Summ->getRetEffect() : RetEffect::MakeNoRet();
|
2008-04-12 00:23:24 +04:00
|
|
|
}
|
|
|
|
|
2008-05-06 06:41:27 +04:00
|
|
|
static inline ArgEffect GetReceiverE(RetainSummary* Summ) {
|
|
|
|
return Summ ? Summ->getReceiverEffect() : DoNothing;
|
|
|
|
}
|
|
|
|
|
2008-07-18 21:24:20 +04:00
|
|
|
static inline bool IsEndPath(RetainSummary* Summ) {
|
|
|
|
return Summ ? Summ->isEndPath() : false;
|
|
|
|
}
|
|
|
|
|
2008-04-16 08:28:53 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
/// GetReturnType - Used to get the return type of a message expression or
|
|
|
|
/// function call with the intention of affixing that type to a tracked symbol.
|
|
|
|
/// While the the return type can be queried directly from RetEx, when
|
|
|
|
/// invoking class methods we augment to the return type to be that of
|
|
|
|
/// a pointer to the class (as opposed it just being id).
|
|
|
|
static QualType GetReturnType(Expr* RetE, ASTContext& Ctx) {
|
|
|
|
|
|
|
|
QualType RetTy = RetE->getType();
|
|
|
|
|
|
|
|
// FIXME: We aren't handling id<...>.
|
2008-07-27 02:36:27 +04:00
|
|
|
const PointerType* PT = RetTy->getAsPointerType();
|
2008-06-26 01:21:56 +04:00
|
|
|
if (!PT)
|
|
|
|
return RetTy;
|
|
|
|
|
|
|
|
// If RetEx is not a message expression just return its type.
|
|
|
|
// If RetEx is a message expression, return its types if it is something
|
|
|
|
/// more specific than id.
|
|
|
|
|
|
|
|
ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(RetE);
|
|
|
|
|
2009-02-12 20:52:19 +03:00
|
|
|
if (!ME || !Ctx.isObjCIdStructType(PT->getPointeeType()))
|
2008-06-26 01:21:56 +04:00
|
|
|
return RetTy;
|
|
|
|
|
|
|
|
ObjCInterfaceDecl* D = ME->getClassInfo().first;
|
|
|
|
|
|
|
|
// At this point we know the return type of the message expression is id.
|
|
|
|
// If we have an ObjCInterceDecl, we know this is a call to a class method
|
|
|
|
// whose type we can resolve. In such cases, promote the return type to
|
|
|
|
// Class*.
|
|
|
|
return !D ? RetTy : Ctx.getPointerType(Ctx.getObjCInterfaceType(D));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-13 08:27:00 +04:00
|
|
|
void CFRefCount::EvalSummary(ExplodedNodeSet<GRState>& Dst,
|
2008-05-06 02:11:16 +04:00
|
|
|
GRExprEngine& Eng,
|
2008-08-13 08:27:00 +04:00
|
|
|
GRStmtNodeBuilder<GRState>& Builder,
|
2008-05-06 02:11:16 +04:00
|
|
|
Expr* Ex,
|
|
|
|
Expr* Receiver,
|
|
|
|
RetainSummary* Summ,
|
This patch is motivated by numerous strict-aliasing warnings when compiling
clang as a Release build.
The big change is that all AST nodes (subclasses of Stmt) whose children are
Expr* store their children as Stmt* or arrays of Stmt*. This is to remove
strict-aliasing warnings when using StmtIterator. None of the interfaces of any
of the classes have changed (except those with arg_iterators, see below), as the
accessor methods introduce the needed casts (via cast<>). While this extra
casting may seem cumbersome, it actually adds some important sanity checks
throughout the codebase, as clients using StmtIterator can potentially overwrite
children that are expected to be Expr* with Stmt* (that aren't Expr*). The casts
provide extra sanity checks that are operational in debug builds to catch
invariant violations such as these.
For classes that have arg_iterators (e.g., CallExpr), the definition of
arg_iterator has been replaced. Instead of it being Expr**, it is an actual
class (called ExprIterator) that wraps a Stmt**, and provides the necessary
operators for iteration. The nice thing about this class is that it also uses
cast<> to type-checking, which introduces extra sanity checks throughout the
codebase that are useful for debugging.
A few of the CodeGen functions that use arg_iterator (especially from
OverloadExpr) have been modified to take begin and end iterators instead of a
base Expr** and the number of arguments. This matches more with the abstraction
of iteration. This still needs to be cleaned up a little bit, as clients expect
that ExprIterator is a RandomAccessIterator (which we may or may not wish to
allow for efficiency of representation).
This is a fairly large patch. It passes the tests (except CodeGen/bitfield.c,
which was already broken) on both a Debug and Release build, but it should
obviously be reviewed.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@52378 91177308-0d34-0410-b5e6-96231b3b80d8
2008-06-17 06:43:46 +04:00
|
|
|
ExprIterator arg_beg, ExprIterator arg_end,
|
2008-08-13 08:27:00 +04:00
|
|
|
ExplodedNode<GRState>* Pred) {
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
// Get the state.
|
2008-08-15 01:16:54 +04:00
|
|
|
GRStateRef state(Builder.GetState(Pred), Eng.getStateManager());
|
2008-10-25 00:32:50 +04:00
|
|
|
ASTContext& Ctx = Eng.getStateManager().getContext();
|
2008-05-06 06:41:27 +04:00
|
|
|
|
|
|
|
// Evaluate the effect of the arguments.
|
2008-04-16 08:28:53 +04:00
|
|
|
RefVal::Kind hasErr = (RefVal::Kind) 0;
|
2008-03-12 04:21:45 +03:00
|
|
|
unsigned idx = 0;
|
2008-04-11 22:40:51 +04:00
|
|
|
Expr* ErrorExpr = NULL;
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef ErrorSym = 0;
|
2008-04-11 22:40:51 +04:00
|
|
|
|
2008-08-15 01:16:54 +04:00
|
|
|
for (ExprIterator I = arg_beg; I != arg_end; ++I, ++idx) {
|
2008-10-17 09:57:07 +04:00
|
|
|
SVal V = state.GetSVal(*I);
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-10-17 09:57:07 +04:00
|
|
|
if (isa<loc::SymbolVal>(V)) {
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef Sym = cast<loc::SymbolVal>(V).getSymbol();
|
2008-08-15 01:16:54 +04:00
|
|
|
if (RefBindings::data_type* T = state.get<RefBindings>(Sym))
|
|
|
|
if (Update(state, Sym, *T, GetArgE(Summ, idx), hasErr)) {
|
2008-04-11 22:40:51 +04:00
|
|
|
ErrorExpr = *I;
|
2008-07-07 20:21:19 +04:00
|
|
|
ErrorSym = Sym;
|
2008-04-11 22:40:51 +04:00
|
|
|
break;
|
|
|
|
}
|
2008-04-12 00:51:02 +04:00
|
|
|
}
|
2008-10-17 09:57:07 +04:00
|
|
|
else if (isa<Loc>(V)) {
|
|
|
|
if (loc::MemRegionVal* MR = dyn_cast<loc::MemRegionVal>(&V)) {
|
2008-07-09 22:11:16 +04:00
|
|
|
|
|
|
|
if (GetArgE(Summ, idx) == DoNothingByRef)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Invalidate the value of the variable passed by reference.
|
2008-07-04 03:26:32 +04:00
|
|
|
|
|
|
|
// FIXME: Either this logic should also be replicated in GRSimpleVals
|
|
|
|
// or should be pulled into a separate "constraint engine."
|
2008-07-09 22:11:16 +04:00
|
|
|
|
2008-07-04 03:26:32 +04:00
|
|
|
// FIXME: We can have collisions on the conjured symbol if the
|
|
|
|
// expression *I also creates conjured symbols. We probably want
|
|
|
|
// to identify conjured symbols by an expression pair: the enclosing
|
|
|
|
// expression (the context) and the expression itself. This should
|
2008-07-09 22:11:16 +04:00
|
|
|
// disambiguate conjured symbols.
|
2008-10-04 09:50:14 +04:00
|
|
|
|
2008-10-18 00:28:54 +04:00
|
|
|
const TypedRegion* R = dyn_cast<TypedRegion>(MR->getRegion());
|
2008-12-17 22:42:34 +03:00
|
|
|
|
|
|
|
// Blast through AnonTypedRegions to get the original region type.
|
|
|
|
while (R) {
|
|
|
|
const AnonTypedRegion* ATR = dyn_cast<AnonTypedRegion>(R);
|
|
|
|
if (!ATR) break;
|
|
|
|
R = dyn_cast<TypedRegion>(ATR->getSuperRegion());
|
|
|
|
}
|
|
|
|
|
2008-10-04 09:50:14 +04:00
|
|
|
if (R) {
|
2008-12-19 02:34:57 +03:00
|
|
|
|
|
|
|
// Is the invalidated variable something that we were tracking?
|
|
|
|
SVal X = state.GetSVal(Loc::MakeVal(R));
|
|
|
|
|
|
|
|
if (isa<loc::SymbolVal>(X)) {
|
|
|
|
SymbolRef Sym = cast<loc::SymbolVal>(X).getSymbol();
|
|
|
|
state = state.remove<RefBindings>(Sym);
|
|
|
|
}
|
|
|
|
|
2008-10-04 09:50:14 +04:00
|
|
|
// Set the value of the variable to be a conjured symbol.
|
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
2008-12-14 00:49:13 +03:00
|
|
|
QualType T = R->getRValueType(Ctx);
|
2008-10-04 09:50:14 +04:00
|
|
|
|
2008-10-18 02:23:12 +04:00
|
|
|
// FIXME: handle structs.
|
2008-11-13 09:10:40 +03:00
|
|
|
if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())) {
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef NewSym =
|
2008-10-18 02:23:12 +04:00
|
|
|
Eng.getSymbolManager().getConjuredSymbol(*I, T, Count);
|
|
|
|
|
2008-12-17 22:42:34 +03:00
|
|
|
state = state.BindLoc(Loc::MakeVal(R),
|
2008-10-18 02:23:12 +04:00
|
|
|
Loc::IsLocType(T)
|
|
|
|
? cast<SVal>(loc::SymbolVal(NewSym))
|
|
|
|
: cast<SVal>(nonloc::SymbolVal(NewSym)));
|
|
|
|
}
|
|
|
|
else {
|
2008-11-12 22:22:09 +03:00
|
|
|
state = state.BindLoc(*MR, UnknownVal());
|
2008-10-18 02:23:12 +04:00
|
|
|
}
|
2008-10-04 09:50:14 +04:00
|
|
|
}
|
|
|
|
else
|
2008-11-12 22:22:09 +03:00
|
|
|
state = state.BindLoc(*MR, UnknownVal());
|
2008-07-04 03:26:32 +04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Nuke all other arguments passed by reference.
|
2008-10-17 09:57:07 +04:00
|
|
|
state = state.Unbind(cast<Loc>(V));
|
2008-07-04 03:26:32 +04:00
|
|
|
}
|
2008-04-12 00:51:02 +04:00
|
|
|
}
|
2008-10-17 09:57:07 +04:00
|
|
|
else if (isa<nonloc::LocAsInteger>(V))
|
|
|
|
state = state.Unbind(cast<nonloc::LocAsInteger>(V).getLoc());
|
2008-05-06 02:11:16 +04:00
|
|
|
}
|
2008-04-16 08:28:53 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
// Evaluate the effect on the message receiver.
|
2008-05-06 06:41:27 +04:00
|
|
|
if (!ErrorExpr && Receiver) {
|
2008-10-17 09:57:07 +04:00
|
|
|
SVal V = state.GetSVal(Receiver);
|
|
|
|
if (isa<loc::SymbolVal>(V)) {
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef Sym = cast<loc::SymbolVal>(V).getSymbol();
|
2008-08-15 01:16:54 +04:00
|
|
|
if (const RefVal* T = state.get<RefBindings>(Sym))
|
|
|
|
if (Update(state, Sym, *T, GetReceiverE(Summ), hasErr)) {
|
2008-05-06 06:41:27 +04:00
|
|
|
ErrorExpr = Receiver;
|
2008-07-07 20:21:19 +04:00
|
|
|
ErrorSym = Sym;
|
2008-05-06 06:41:27 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
// Process any errors.
|
2008-04-16 08:28:53 +04:00
|
|
|
if (hasErr) {
|
2008-08-15 01:16:54 +04:00
|
|
|
ProcessNonLeakError(Dst, Builder, Ex, ErrorExpr, Pred, state,
|
2008-04-18 07:39:05 +04:00
|
|
|
hasErr, ErrorSym);
|
2008-03-12 04:21:45 +03:00
|
|
|
return;
|
2008-03-11 20:48:22 +03:00
|
|
|
}
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-07-18 21:24:20 +04:00
|
|
|
// Consult the summary for the return value.
|
2008-05-06 06:26:56 +04:00
|
|
|
RetEffect RE = GetRetEffect(Summ);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
switch (RE.getKind()) {
|
|
|
|
default:
|
|
|
|
assert (false && "Unhandled RetEffect."); break;
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-10-18 02:23:12 +04:00
|
|
|
case RetEffect::NoRet: {
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-04-12 00:23:24 +04:00
|
|
|
// Make up a symbol for the return value (not reference counted).
|
2008-04-12 00:51:02 +04:00
|
|
|
// FIXME: This is basically copy-and-paste from GRSimpleVals. We
|
|
|
|
// should compose behavior, not copy it.
|
2008-04-12 00:23:24 +04:00
|
|
|
|
2008-10-18 02:23:12 +04:00
|
|
|
// FIXME: We eventually should handle structs and other compound types
|
|
|
|
// that are returned by value.
|
|
|
|
|
|
|
|
QualType T = Ex->getType();
|
|
|
|
|
2008-11-13 09:10:40 +03:00
|
|
|
if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())) {
|
2008-04-12 00:23:24 +04:00
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
|
2008-04-12 00:23:24 +04:00
|
|
|
|
2009-02-20 03:05:35 +03:00
|
|
|
SVal X = Loc::IsLocType(T)
|
2008-10-17 09:57:07 +04:00
|
|
|
? cast<SVal>(loc::SymbolVal(Sym))
|
|
|
|
: cast<SVal>(nonloc::SymbolVal(Sym));
|
2008-04-12 00:23:24 +04:00
|
|
|
|
2008-11-12 22:22:09 +03:00
|
|
|
state = state.BindExpr(Ex, X, false);
|
2008-04-12 00:23:24 +04:00
|
|
|
}
|
|
|
|
|
2008-04-11 03:44:06 +04:00
|
|
|
break;
|
2008-10-18 02:23:12 +04:00
|
|
|
}
|
2008-04-11 03:44:06 +04:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
case RetEffect::Alias: {
|
2008-06-26 01:21:56 +04:00
|
|
|
unsigned idx = RE.getIndex();
|
This patch is motivated by numerous strict-aliasing warnings when compiling
clang as a Release build.
The big change is that all AST nodes (subclasses of Stmt) whose children are
Expr* store their children as Stmt* or arrays of Stmt*. This is to remove
strict-aliasing warnings when using StmtIterator. None of the interfaces of any
of the classes have changed (except those with arg_iterators, see below), as the
accessor methods introduce the needed casts (via cast<>). While this extra
casting may seem cumbersome, it actually adds some important sanity checks
throughout the codebase, as clients using StmtIterator can potentially overwrite
children that are expected to be Expr* with Stmt* (that aren't Expr*). The casts
provide extra sanity checks that are operational in debug builds to catch
invariant violations such as these.
For classes that have arg_iterators (e.g., CallExpr), the definition of
arg_iterator has been replaced. Instead of it being Expr**, it is an actual
class (called ExprIterator) that wraps a Stmt**, and provides the necessary
operators for iteration. The nice thing about this class is that it also uses
cast<> to type-checking, which introduces extra sanity checks throughout the
codebase that are useful for debugging.
A few of the CodeGen functions that use arg_iterator (especially from
OverloadExpr) have been modified to take begin and end iterators instead of a
base Expr** and the number of arguments. This matches more with the abstraction
of iteration. This still needs to be cleaned up a little bit, as clients expect
that ExprIterator is a RandomAccessIterator (which we may or may not wish to
allow for efficiency of representation).
This is a fairly large patch. It passes the tests (except CodeGen/bitfield.c,
which was already broken) on both a Debug and Release build, but it should
obviously be reviewed.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@52378 91177308-0d34-0410-b5e6-96231b3b80d8
2008-06-17 06:43:46 +04:00
|
|
|
assert (arg_end >= arg_beg);
|
2008-05-06 02:11:16 +04:00
|
|
|
assert (idx < (unsigned) (arg_end - arg_beg));
|
2008-10-17 09:57:07 +04:00
|
|
|
SVal V = state.GetSVal(*(arg_beg+idx));
|
2008-11-12 22:22:09 +03:00
|
|
|
state = state.BindExpr(Ex, V, false);
|
2008-03-12 04:21:45 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-05-06 06:41:27 +04:00
|
|
|
case RetEffect::ReceiverAlias: {
|
|
|
|
assert (Receiver);
|
2008-10-17 09:57:07 +04:00
|
|
|
SVal V = state.GetSVal(Receiver);
|
2008-11-12 22:22:09 +03:00
|
|
|
state = state.BindExpr(Ex, V, false);
|
2008-05-06 06:41:27 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-06-23 22:02:52 +04:00
|
|
|
case RetEffect::OwnedAllocatedSymbol:
|
2008-03-12 04:21:45 +03:00
|
|
|
case RetEffect::OwnedSymbol: {
|
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
|
2009-01-28 08:56:51 +03:00
|
|
|
QualType RetT = GetReturnType(Ex, Eng.getContext());
|
|
|
|
state =
|
|
|
|
state.set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(), RetT));
|
2008-11-12 22:22:09 +03:00
|
|
|
state = state.BindExpr(Ex, loc::SymbolVal(Sym), false);
|
2008-08-15 01:16:54 +04:00
|
|
|
|
2008-06-23 22:02:52 +04:00
|
|
|
// FIXME: Add a flag to the checker where allocations are allowed to fail.
|
2009-01-29 01:27:59 +03:00
|
|
|
if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) {
|
|
|
|
bool isFeasible;
|
|
|
|
state = state.Assume(loc::SymbolVal(Sym), true, isFeasible);
|
|
|
|
assert(isFeasible && "Cannot assume fresh symbol is non-null.");
|
|
|
|
}
|
2008-06-23 22:02:52 +04:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RetEffect::NotOwnedSymbol: {
|
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
|
2008-06-26 01:21:56 +04:00
|
|
|
QualType RetT = GetReturnType(Ex, Eng.getContext());
|
2008-03-12 04:21:45 +03:00
|
|
|
|
2009-01-28 08:56:51 +03:00
|
|
|
state =
|
|
|
|
state.set<RefBindings>(Sym, RefVal::makeNotOwned(RE.getObjKind(),RetT));
|
2008-11-12 22:22:09 +03:00
|
|
|
state = state.BindExpr(Ex, loc::SymbolVal(Sym), false);
|
2008-03-12 04:21:45 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2009-02-18 05:00:25 +03:00
|
|
|
// Generate a sink node if we are at the end of a path.
|
|
|
|
GRExprEngine::NodeTy *NewNode =
|
|
|
|
IsEndPath(Summ) ? Builder.MakeSinkNode(Dst, Ex, Pred, state)
|
|
|
|
: Builder.MakeNode(Dst, Ex, Pred, state);
|
|
|
|
|
|
|
|
// Annotate the edge with summary we used.
|
|
|
|
// FIXME: This assumes that we always use the same summary when generating
|
|
|
|
// this node.
|
|
|
|
if (NewNode) SummaryLog[NewNode] = Summ;
|
2008-05-06 02:11:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-13 08:27:00 +04:00
|
|
|
void CFRefCount::EvalCall(ExplodedNodeSet<GRState>& Dst,
|
2008-05-06 02:11:16 +04:00
|
|
|
GRExprEngine& Eng,
|
2008-08-13 08:27:00 +04:00
|
|
|
GRStmtNodeBuilder<GRState>& Builder,
|
2008-10-17 09:57:07 +04:00
|
|
|
CallExpr* CE, SVal L,
|
2008-08-13 08:27:00 +04:00
|
|
|
ExplodedNode<GRState>* Pred) {
|
2008-05-06 02:11:16 +04:00
|
|
|
|
2008-10-17 09:57:07 +04:00
|
|
|
RetainSummary* Summ = !isa<loc::FuncVal>(L) ? 0
|
|
|
|
: Summaries.getSummary(cast<loc::FuncVal>(L).getDecl());
|
2008-05-06 02:11:16 +04:00
|
|
|
|
|
|
|
EvalSummary(Dst, Eng, Builder, CE, 0, Summ,
|
|
|
|
CE->arg_begin(), CE->arg_end(), Pred);
|
2008-03-06 03:08:09 +03:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-08-13 08:27:00 +04:00
|
|
|
void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet<GRState>& Dst,
|
2008-04-16 03:44:31 +04:00
|
|
|
GRExprEngine& Eng,
|
2008-08-13 08:27:00 +04:00
|
|
|
GRStmtNodeBuilder<GRState>& Builder,
|
2008-04-16 03:44:31 +04:00
|
|
|
ObjCMessageExpr* ME,
|
2008-08-13 08:27:00 +04:00
|
|
|
ExplodedNode<GRState>* Pred) {
|
2008-05-06 08:20:12 +04:00
|
|
|
RetainSummary* Summ;
|
2008-05-02 01:31:50 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
if (Expr* Receiver = ME->getReceiver()) {
|
|
|
|
// We need the type-information of the tracked receiver object
|
|
|
|
// Retrieve it from the state.
|
|
|
|
ObjCInterfaceDecl* ID = 0;
|
|
|
|
|
|
|
|
// FIXME: Wouldn't it be great if this code could be reduced? It's just
|
|
|
|
// a chain of lookups.
|
2008-08-13 08:27:00 +04:00
|
|
|
const GRState* St = Builder.GetState(Pred);
|
2008-10-17 09:57:07 +04:00
|
|
|
SVal V = Eng.getStateManager().GetSVal(St, Receiver );
|
2008-06-26 01:21:56 +04:00
|
|
|
|
2008-10-17 09:57:07 +04:00
|
|
|
if (isa<loc::SymbolVal>(V)) {
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef Sym = cast<loc::SymbolVal>(V).getSymbol();
|
2008-06-26 01:21:56 +04:00
|
|
|
|
2008-08-15 01:16:54 +04:00
|
|
|
if (const RefVal* T = St->get<RefBindings>(Sym)) {
|
2008-07-07 20:21:19 +04:00
|
|
|
QualType Ty = T->getType();
|
2008-06-26 01:21:56 +04:00
|
|
|
|
|
|
|
if (const PointerType* PT = Ty->getAsPointerType()) {
|
|
|
|
QualType PointeeTy = PT->getPointeeType();
|
|
|
|
|
|
|
|
if (ObjCInterfaceType* IT = dyn_cast<ObjCInterfaceType>(PointeeTy))
|
|
|
|
ID = IT->getDecl();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Summ = Summaries.getMethodSummary(ME, ID);
|
2008-10-25 00:32:50 +04:00
|
|
|
|
2008-10-23 05:56:15 +04:00
|
|
|
// Special-case: are we sending a mesage to "self"?
|
|
|
|
// This is a hack. When we have full-IP this should be removed.
|
|
|
|
if (!Summ) {
|
|
|
|
ObjCMethodDecl* MD =
|
|
|
|
dyn_cast<ObjCMethodDecl>(&Eng.getGraph().getCodeDecl());
|
|
|
|
|
|
|
|
if (MD) {
|
|
|
|
if (Expr* Receiver = ME->getReceiver()) {
|
|
|
|
SVal X = Eng.getStateManager().GetSVal(St, Receiver);
|
|
|
|
if (loc::MemRegionVal* L = dyn_cast<loc::MemRegionVal>(&X))
|
2008-10-25 00:32:50 +04:00
|
|
|
if (L->getRegion() == Eng.getStateManager().getSelfRegion(St)) {
|
|
|
|
// Create a summmary where all of the arguments "StopTracking".
|
|
|
|
Summ = Summaries.getPersistentSummary(RetEffect::MakeNoRet(),
|
|
|
|
DoNothing,
|
|
|
|
StopTracking);
|
|
|
|
}
|
2008-10-23 05:56:15 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-06-26 01:21:56 +04:00
|
|
|
}
|
2008-04-16 08:28:53 +04:00
|
|
|
else
|
2008-06-24 02:21:20 +04:00
|
|
|
Summ = Summaries.getClassMethodSummary(ME->getClassName(),
|
|
|
|
ME->getSelector());
|
2008-04-16 08:28:53 +04:00
|
|
|
|
2008-05-06 08:20:12 +04:00
|
|
|
EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), Summ,
|
|
|
|
ME->arg_begin(), ME->arg_end(), Pred);
|
2008-04-16 03:44:31 +04:00
|
|
|
}
|
2009-02-14 06:16:10 +03:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
class VISIBILITY_HIDDEN StopTrackingCallback : public SymbolVisitor {
|
|
|
|
GRStateRef state;
|
|
|
|
public:
|
|
|
|
StopTrackingCallback(GRStateRef st) : state(st) {}
|
|
|
|
GRStateRef getState() { return state; }
|
|
|
|
|
|
|
|
bool VisitSymbol(SymbolRef sym) {
|
|
|
|
state = state.remove<RefBindings>(sym);
|
|
|
|
return true;
|
|
|
|
}
|
2008-05-06 08:20:12 +04:00
|
|
|
|
2009-02-14 06:16:10 +03:00
|
|
|
const GRState* getState() const { return state.getState(); }
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
2008-04-17 00:40:59 +04:00
|
|
|
|
2009-02-14 06:16:10 +03:00
|
|
|
|
|
|
|
void CFRefCount::EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val) {
|
2009-02-14 04:43:44 +03:00
|
|
|
// Are we storing to something that causes the value to "escape"?
|
2008-04-17 00:40:59 +04:00
|
|
|
bool escapes = false;
|
|
|
|
|
2008-10-18 07:49:51 +04:00
|
|
|
// A value escapes in three possible cases (this may change):
|
|
|
|
//
|
|
|
|
// (1) we are binding to something that is not a memory region.
|
|
|
|
// (2) we are binding to a memregion that does not have stack storage
|
|
|
|
// (3) we are binding to a memregion with stack storage that the store
|
2009-02-14 04:43:44 +03:00
|
|
|
// does not understand.
|
|
|
|
GRStateRef state = B.getState();
|
2008-10-18 07:49:51 +04:00
|
|
|
|
2009-02-14 04:43:44 +03:00
|
|
|
if (!isa<loc::MemRegionVal>(location))
|
2008-04-17 00:40:59 +04:00
|
|
|
escapes = true;
|
2008-10-04 09:50:14 +04:00
|
|
|
else {
|
2009-02-14 04:43:44 +03:00
|
|
|
const MemRegion* R = cast<loc::MemRegionVal>(location).getRegion();
|
|
|
|
escapes = !B.getStateManager().hasStackStorage(R);
|
2008-10-18 07:49:51 +04:00
|
|
|
|
|
|
|
if (!escapes) {
|
|
|
|
// To test (3), generate a new state with the binding removed. If it is
|
|
|
|
// the same state, then it escapes (since the store cannot represent
|
|
|
|
// the binding).
|
2009-02-14 04:43:44 +03:00
|
|
|
escapes = (state == (state.BindLoc(cast<Loc>(location), UnknownVal())));
|
2008-10-18 07:49:51 +04:00
|
|
|
}
|
2008-10-04 09:50:14 +04:00
|
|
|
}
|
2008-10-18 07:49:51 +04:00
|
|
|
|
2009-02-14 06:16:10 +03:00
|
|
|
// If our store can represent the binding and we aren't storing to something
|
|
|
|
// that doesn't have local storage then just return and have the simulation
|
|
|
|
// state continue as is.
|
|
|
|
if (!escapes)
|
|
|
|
return;
|
2008-04-17 00:40:59 +04:00
|
|
|
|
2009-02-14 06:16:10 +03:00
|
|
|
// Otherwise, find all symbols referenced by 'val' that we are tracking
|
|
|
|
// and stop tracking them.
|
|
|
|
B.MakeNode(state.scanReachableSymbols<StopTrackingCallback>(val).getState());
|
2008-04-17 02:32:20 +04:00
|
|
|
}
|
|
|
|
|
2008-10-25 00:32:50 +04:00
|
|
|
std::pair<GRStateRef,bool>
|
|
|
|
CFRefCount::HandleSymbolDeath(GRStateManager& VMgr,
|
|
|
|
const GRState* St, const Decl* CD,
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef sid,
|
2008-10-25 00:32:50 +04:00
|
|
|
RefVal V, bool& hasLeak) {
|
2008-04-17 02:32:20 +04:00
|
|
|
|
2008-08-15 01:16:54 +04:00
|
|
|
GRStateRef state(St, VMgr);
|
2008-10-31 12:52:39 +03:00
|
|
|
assert ((!V.isReturnedOwned() || CD) &&
|
2008-10-23 03:56:21 +04:00
|
|
|
"CodeDecl must be available for reporting ReturnOwned errors.");
|
2008-10-23 05:56:15 +04:00
|
|
|
|
2008-10-23 03:56:21 +04:00
|
|
|
if (V.isReturnedOwned() && V.getCount() == 0)
|
|
|
|
if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) {
|
2008-11-24 06:33:13 +03:00
|
|
|
std::string s = MD->getSelector().getAsString();
|
2008-11-05 19:54:44 +03:00
|
|
|
if (!followsReturnRule(s.c_str())) {
|
2008-10-23 03:56:21 +04:00
|
|
|
hasLeak = true;
|
2008-10-25 00:32:50 +04:00
|
|
|
state = state.set<RefBindings>(sid, V ^ RefVal::ErrorLeakReturned);
|
|
|
|
return std::make_pair(state, true);
|
2008-10-23 03:56:21 +04:00
|
|
|
}
|
|
|
|
}
|
2008-10-23 05:56:15 +04:00
|
|
|
|
2008-10-23 03:56:21 +04:00
|
|
|
// All other cases.
|
|
|
|
|
|
|
|
hasLeak = V.isOwned() ||
|
|
|
|
((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0);
|
2008-08-15 01:16:54 +04:00
|
|
|
|
2008-04-17 02:32:20 +04:00
|
|
|
if (!hasLeak)
|
2008-10-25 00:32:50 +04:00
|
|
|
return std::make_pair(state.remove<RefBindings>(sid), false);
|
2008-05-05 21:53:17 +04:00
|
|
|
|
2008-10-25 00:32:50 +04:00
|
|
|
return std::make_pair(state.set<RefBindings>(sid, V ^ RefVal::ErrorLeak),
|
|
|
|
false);
|
2008-04-17 02:32:20 +04:00
|
|
|
}
|
|
|
|
|
2008-04-25 03:57:27 +04:00
|
|
|
|
2008-04-12 02:25:11 +04:00
|
|
|
|
2008-04-25 03:57:27 +04:00
|
|
|
// Dead symbols.
|
|
|
|
|
2009-02-05 09:50:21 +03:00
|
|
|
|
2008-04-25 03:57:27 +04:00
|
|
|
|
2008-04-17 22:12:53 +04:00
|
|
|
// Return statements.
|
|
|
|
|
2008-08-13 08:27:00 +04:00
|
|
|
void CFRefCount::EvalReturn(ExplodedNodeSet<GRState>& Dst,
|
2008-04-17 22:12:53 +04:00
|
|
|
GRExprEngine& Eng,
|
2008-08-13 08:27:00 +04:00
|
|
|
GRStmtNodeBuilder<GRState>& Builder,
|
2008-04-17 22:12:53 +04:00
|
|
|
ReturnStmt* S,
|
2008-08-13 08:27:00 +04:00
|
|
|
ExplodedNode<GRState>* Pred) {
|
2008-04-17 22:12:53 +04:00
|
|
|
|
|
|
|
Expr* RetE = S->getRetValue();
|
|
|
|
if (!RetE) return;
|
|
|
|
|
2008-08-15 01:16:54 +04:00
|
|
|
GRStateRef state(Builder.GetState(Pred), Eng.getStateManager());
|
2008-10-17 09:57:07 +04:00
|
|
|
SVal V = state.GetSVal(RetE);
|
2008-04-17 22:12:53 +04:00
|
|
|
|
2008-10-17 09:57:07 +04:00
|
|
|
if (!isa<loc::SymbolVal>(V))
|
2008-04-17 22:12:53 +04:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Get the reference count binding (if any).
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef Sym = cast<loc::SymbolVal>(V).getSymbol();
|
2008-08-15 01:16:54 +04:00
|
|
|
const RefVal* T = state.get<RefBindings>(Sym);
|
2008-04-17 22:12:53 +04:00
|
|
|
|
|
|
|
if (!T)
|
|
|
|
return;
|
|
|
|
|
2008-08-15 01:16:54 +04:00
|
|
|
// Change the reference count.
|
2008-07-07 20:21:19 +04:00
|
|
|
RefVal X = *T;
|
2008-04-17 22:12:53 +04:00
|
|
|
|
2008-08-15 01:16:54 +04:00
|
|
|
switch (X.getKind()) {
|
2008-04-17 22:12:53 +04:00
|
|
|
case RefVal::Owned: {
|
|
|
|
unsigned cnt = X.getCount();
|
2008-05-22 21:31:13 +04:00
|
|
|
assert (cnt > 0);
|
|
|
|
X = RefVal::makeReturnedOwned(cnt - 1);
|
2008-04-17 22:12:53 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RefVal::NotOwned: {
|
|
|
|
unsigned cnt = X.getCount();
|
|
|
|
X = cnt ? RefVal::makeReturnedOwned(cnt - 1)
|
|
|
|
: RefVal::makeReturnedNotOwned();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the binding.
|
2008-08-17 07:20:02 +04:00
|
|
|
state = state.set<RefBindings>(Sym, X);
|
2008-08-15 01:16:54 +04:00
|
|
|
Builder.MakeNode(Dst, S, Pred, state);
|
2008-04-17 22:12:53 +04:00
|
|
|
}
|
|
|
|
|
2008-04-18 23:23:43 +04:00
|
|
|
// Assumptions.
|
|
|
|
|
2008-08-13 08:27:00 +04:00
|
|
|
const GRState* CFRefCount::EvalAssume(GRStateManager& VMgr,
|
|
|
|
const GRState* St,
|
2008-10-17 09:57:07 +04:00
|
|
|
SVal Cond, bool Assumption,
|
2008-07-11 02:03:41 +04:00
|
|
|
bool& isFeasible) {
|
2008-04-18 23:23:43 +04:00
|
|
|
|
|
|
|
// FIXME: We may add to the interface of EvalAssume the list of symbols
|
|
|
|
// whose assumptions have changed. For now we just iterate through the
|
|
|
|
// bindings and check if any of the tracked symbols are NULL. This isn't
|
|
|
|
// too bad since the number of symbols we will track in practice are
|
|
|
|
// probably small and EvalAssume is only called at branches and a few
|
|
|
|
// other places.
|
2008-08-15 01:16:54 +04:00
|
|
|
RefBindings B = St->get<RefBindings>();
|
2008-04-18 23:23:43 +04:00
|
|
|
|
|
|
|
if (B.isEmpty())
|
|
|
|
return St;
|
|
|
|
|
|
|
|
bool changed = false;
|
2008-08-17 07:20:02 +04:00
|
|
|
|
|
|
|
GRStateRef state(St, VMgr);
|
|
|
|
RefBindings::Factory& RefBFactory = state.get_context<RefBindings>();
|
2008-04-18 23:23:43 +04:00
|
|
|
|
|
|
|
for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
|
|
|
|
// Check if the symbol is null (or equal to any constant).
|
|
|
|
// If this is the case, stop tracking the symbol.
|
2008-08-29 18:52:36 +04:00
|
|
|
if (VMgr.getSymVal(St, I.getKey())) {
|
2008-04-18 23:23:43 +04:00
|
|
|
changed = true;
|
|
|
|
B = RefBFactory.Remove(B, I.getKey());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-17 07:20:02 +04:00
|
|
|
if (changed)
|
|
|
|
state = state.set<RefBindings>(B);
|
2008-04-18 23:23:43 +04:00
|
|
|
|
2008-08-15 01:16:54 +04:00
|
|
|
return state;
|
2008-04-18 23:23:43 +04:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-12-05 05:27:51 +03:00
|
|
|
RefBindings CFRefCount::Update(RefBindings B, SymbolRef sym,
|
2008-08-15 01:16:54 +04:00
|
|
|
RefVal V, ArgEffect E,
|
2008-08-17 07:20:02 +04:00
|
|
|
RefVal::Kind& hasErr,
|
|
|
|
RefBindings::Factory& RefBFactory) {
|
2009-02-18 21:54:33 +03:00
|
|
|
|
|
|
|
// 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;
|
2009-02-19 00:57:45 +03:00
|
|
|
case MakeCollectable: E = isGCEnabled() ? DecRef : DoNothing; break;
|
2009-02-18 21:54:33 +03:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
switch (E) {
|
|
|
|
default:
|
|
|
|
assert (false && "Unhandled CFRef transition.");
|
2008-05-22 21:31:13 +04:00
|
|
|
|
|
|
|
case MayEscape:
|
|
|
|
if (V.getKind() == RefVal::Owned) {
|
2008-06-26 01:21:56 +04:00
|
|
|
V = V ^ RefVal::NotOwned;
|
2008-05-22 21:31:13 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Fall-through.
|
2008-07-09 22:11:16 +04:00
|
|
|
case DoNothingByRef:
|
2008-03-11 20:48:22 +03:00
|
|
|
case DoNothing:
|
2008-05-06 02:11:16 +04:00
|
|
|
if (!isGCEnabled() && V.getKind() == RefVal::Released) {
|
2008-06-26 01:21:56 +04:00
|
|
|
V = V ^ RefVal::ErrorUseAfterRelease;
|
2008-04-16 08:28:53 +04:00
|
|
|
hasErr = V.getKind();
|
2008-03-12 04:21:45 +03:00
|
|
|
break;
|
2008-08-12 22:30:56 +04:00
|
|
|
}
|
2008-03-11 20:48:22 +03:00
|
|
|
return B;
|
2008-06-30 20:57:41 +04:00
|
|
|
|
2009-01-29 00:44:40 +03:00
|
|
|
case Autorelease:
|
|
|
|
if (isGCEnabled()) return B;
|
|
|
|
// Fall-through.
|
2008-05-06 06:41:27 +04:00
|
|
|
case StopTracking:
|
|
|
|
return RefBFactory.Remove(B, sym);
|
2008-08-12 22:30:56 +04:00
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
case IncRef:
|
|
|
|
switch (V.getKind()) {
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
|
|
|
|
case RefVal::Owned:
|
|
|
|
case RefVal::NotOwned:
|
2008-06-26 01:21:56 +04:00
|
|
|
V = V + 1;
|
2008-08-12 22:30:56 +04:00
|
|
|
break;
|
2008-03-11 20:48:22 +03:00
|
|
|
case RefVal::Released:
|
2008-05-06 02:11:16 +04:00
|
|
|
if (isGCEnabled())
|
2009-02-19 01:57:22 +03:00
|
|
|
V = (V ^ RefVal::Owned) + 1;
|
2008-04-29 09:44:10 +04:00
|
|
|
else {
|
2008-06-26 01:21:56 +04:00
|
|
|
V = V ^ RefVal::ErrorUseAfterRelease;
|
2008-04-29 09:44:10 +04:00
|
|
|
hasErr = V.getKind();
|
|
|
|
}
|
2008-03-11 20:48:22 +03:00
|
|
|
break;
|
2008-08-12 22:30:56 +04:00
|
|
|
}
|
2008-04-11 03:44:06 +04:00
|
|
|
break;
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
case SelfOwn:
|
|
|
|
V = V ^ RefVal::NotOwned;
|
2009-02-18 21:54:33 +03:00
|
|
|
// Fall-through.
|
2008-03-11 20:48:22 +03:00
|
|
|
case DecRef:
|
|
|
|
switch (V.getKind()) {
|
|
|
|
default:
|
|
|
|
assert (false);
|
2008-08-12 22:30:56 +04:00
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
case RefVal::Owned:
|
2009-02-19 01:57:22 +03:00
|
|
|
assert(V.getCount() > 0);
|
|
|
|
if (V.getCount() == 1) V = V ^ RefVal::Released;
|
|
|
|
V = V - 1;
|
2008-03-11 20:48:22 +03:00
|
|
|
break;
|
|
|
|
|
2008-06-26 01:21:56 +04:00
|
|
|
case RefVal::NotOwned:
|
|
|
|
if (V.getCount() > 0)
|
|
|
|
V = V - 1;
|
2008-04-11 03:09:18 +04:00
|
|
|
else {
|
2008-06-26 01:21:56 +04:00
|
|
|
V = V ^ RefVal::ErrorReleaseNotOwned;
|
2008-04-16 08:28:53 +04:00
|
|
|
hasErr = V.getKind();
|
2008-08-12 22:30:56 +04:00
|
|
|
}
|
2008-03-11 20:48:22 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::Released:
|
2008-06-26 01:21:56 +04:00
|
|
|
V = V ^ RefVal::ErrorUseAfterRelease;
|
2008-04-16 08:28:53 +04:00
|
|
|
hasErr = V.getKind();
|
2008-03-11 20:48:22 +03:00
|
|
|
break;
|
2008-08-12 22:30:56 +04:00
|
|
|
}
|
2008-04-11 03:44:06 +04:00
|
|
|
break;
|
2008-03-11 20:48:22 +03:00
|
|
|
}
|
|
|
|
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-18 07:39:05 +04:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
//===-------------===//
|
|
|
|
// Bug Descriptions. //
|
|
|
|
//===-------------===//
|
|
|
|
|
2009-02-05 02:49:09 +03:00
|
|
|
class VISIBILITY_HIDDEN CFRefBug : public BugType {
|
2008-04-18 07:39:05 +04:00
|
|
|
protected:
|
|
|
|
CFRefCount& TF;
|
2009-02-05 02:49:09 +03:00
|
|
|
|
|
|
|
CFRefBug(CFRefCount* tf, const char* name)
|
|
|
|
: BugType(name, "Memory (Core Foundation/Objective-C)"), TF(*tf) {}
|
2008-04-18 07:39:05 +04:00
|
|
|
public:
|
2008-05-01 03:47:44 +04:00
|
|
|
|
2008-05-02 02:50:36 +04:00
|
|
|
CFRefCount& getTF() { return TF; }
|
2008-05-06 03:16:31 +04:00
|
|
|
const CFRefCount& getTF() const { return TF; }
|
|
|
|
|
2009-02-05 02:49:09 +03:00
|
|
|
// FIXME: Eventually remove.
|
|
|
|
virtual const char* getDescription() const = 0;
|
|
|
|
|
2008-05-02 03:13:35 +04:00
|
|
|
virtual bool isLeak() const { return false; }
|
2008-04-18 07:39:05 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
class VISIBILITY_HIDDEN UseAfterRelease : public CFRefBug {
|
|
|
|
public:
|
2009-02-05 02:49:09 +03:00
|
|
|
UseAfterRelease(CFRefCount* tf)
|
|
|
|
: CFRefBug(tf, "use-after-release") {}
|
2008-04-18 07:39:05 +04:00
|
|
|
|
2009-02-05 02:49:09 +03:00
|
|
|
const char* getDescription() const {
|
2008-08-12 22:30:56 +04:00
|
|
|
return "Reference-counted object is used after it is released.";
|
2009-02-05 09:50:21 +03:00
|
|
|
}
|
2008-04-18 07:39:05 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
class VISIBILITY_HIDDEN BadRelease : public CFRefBug {
|
|
|
|
public:
|
2009-02-05 02:49:09 +03:00
|
|
|
BadRelease(CFRefCount* tf) : CFRefBug(tf, "bad release") {}
|
|
|
|
|
|
|
|
const char* getDescription() const {
|
2008-04-18 07:39:05 +04:00
|
|
|
return "Incorrect decrement of the reference count of a "
|
2008-04-18 08:55:01 +04:00
|
|
|
"CoreFoundation object: "
|
2008-04-18 07:39:05 +04:00
|
|
|
"The object is not owned at this point by the caller.";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class VISIBILITY_HIDDEN Leak : public CFRefBug {
|
2009-02-05 02:49:09 +03:00
|
|
|
const bool isReturn;
|
|
|
|
protected:
|
|
|
|
Leak(CFRefCount* tf, const char* name, bool isRet)
|
|
|
|
: CFRefBug(tf, name), isReturn(isRet) {}
|
2008-04-18 07:39:05 +04:00
|
|
|
public:
|
|
|
|
|
2009-02-08 01:38:00 +03:00
|
|
|
const char* getDescription() const { return ""; }
|
2009-01-24 03:55:43 +03:00
|
|
|
|
2009-02-05 03:38:00 +03:00
|
|
|
bool isLeak() const { return true; }
|
2009-02-05 02:49:09 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
class VISIBILITY_HIDDEN LeakAtReturn : public Leak {
|
|
|
|
public:
|
|
|
|
LeakAtReturn(CFRefCount* tf, const char* name)
|
|
|
|
: Leak(tf, name, true) {}
|
2008-04-18 07:39:05 +04:00
|
|
|
};
|
|
|
|
|
2009-02-05 02:49:09 +03:00
|
|
|
class VISIBILITY_HIDDEN LeakWithinFunction : public Leak {
|
|
|
|
public:
|
|
|
|
LeakWithinFunction(CFRefCount* tf, const char* name)
|
|
|
|
: Leak(tf, name, false) {}
|
|
|
|
};
|
|
|
|
|
2008-04-18 07:39:05 +04:00
|
|
|
//===---------===//
|
|
|
|
// Bug Reports. //
|
|
|
|
//===---------===//
|
|
|
|
|
|
|
|
class VISIBILITY_HIDDEN CFRefReport : public RangedBugReport {
|
2009-02-08 01:04:05 +03:00
|
|
|
protected:
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef Sym;
|
2009-02-18 06:48:14 +03:00
|
|
|
const CFRefCount &TF;
|
2008-04-18 07:39:05 +04:00
|
|
|
public:
|
2009-02-18 06:48:14 +03:00
|
|
|
CFRefReport(CFRefBug& D, const CFRefCount &tf,
|
|
|
|
ExplodedNode<GRState> *n, SymbolRef sym)
|
|
|
|
: RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {}
|
2008-04-18 07:39:05 +04:00
|
|
|
|
|
|
|
virtual ~CFRefReport() {}
|
|
|
|
|
2008-05-02 02:50:36 +04:00
|
|
|
CFRefBug& getBugType() {
|
|
|
|
return (CFRefBug&) RangedBugReport::getBugType();
|
|
|
|
}
|
|
|
|
const CFRefBug& getBugType() const {
|
|
|
|
return (const CFRefBug&) RangedBugReport::getBugType();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void getRanges(BugReporter& BR, const SourceRange*& beg,
|
|
|
|
const SourceRange*& end) {
|
|
|
|
|
2008-05-03 00:53:50 +04:00
|
|
|
if (!getBugType().isLeak())
|
2008-05-02 02:50:36 +04:00
|
|
|
RangedBugReport::getRanges(BR, beg, end);
|
2008-08-12 22:30:56 +04:00
|
|
|
else
|
|
|
|
beg = end = 0;
|
2008-05-02 02:50:36 +04:00
|
|
|
}
|
|
|
|
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef getSymbol() const { return Sym; }
|
2008-05-16 22:33:44 +04:00
|
|
|
|
2009-01-24 03:55:43 +03:00
|
|
|
PathDiagnosticPiece* getEndPath(BugReporter& BR,
|
|
|
|
const ExplodedNode<GRState>* N);
|
2008-05-02 03:13:35 +04:00
|
|
|
|
2009-01-24 03:55:43 +03:00
|
|
|
std::pair<const char**,const char**> getExtraDescriptiveText();
|
2008-04-18 07:39:05 +04:00
|
|
|
|
2009-01-24 03:55:43 +03:00
|
|
|
PathDiagnosticPiece* VisitNode(const ExplodedNode<GRState>* N,
|
|
|
|
const ExplodedNode<GRState>* PrevN,
|
|
|
|
const ExplodedGraph<GRState>& G,
|
2009-02-18 06:48:14 +03:00
|
|
|
BugReporter& BR,
|
|
|
|
NodeResolver& NR);
|
2008-04-18 07:39:05 +04:00
|
|
|
};
|
|
|
|
|
2009-02-05 02:49:09 +03:00
|
|
|
class VISIBILITY_HIDDEN CFRefLeakReport : public CFRefReport {
|
2009-02-08 01:19:59 +03:00
|
|
|
SourceLocation AllocSite;
|
|
|
|
const MemRegion* AllocBinding;
|
2009-02-05 02:49:09 +03:00
|
|
|
public:
|
2009-02-18 06:48:14 +03:00
|
|
|
CFRefLeakReport(CFRefBug& D, const CFRefCount &tf,
|
|
|
|
ExplodedNode<GRState> *n, SymbolRef sym,
|
2009-02-08 01:38:00 +03:00
|
|
|
GRExprEngine& Eng);
|
2009-02-08 01:04:05 +03:00
|
|
|
|
|
|
|
PathDiagnosticPiece* getEndPath(BugReporter& BR,
|
|
|
|
const ExplodedNode<GRState>* N);
|
|
|
|
|
2009-02-08 01:19:59 +03:00
|
|
|
SourceLocation getLocation() const { return AllocSite; }
|
2009-02-05 02:49:09 +03:00
|
|
|
};
|
2008-04-18 07:39:05 +04:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
2009-02-05 02:49:09 +03:00
|
|
|
void CFRefCount::RegisterChecks(BugReporter& BR) {
|
2009-02-05 09:50:21 +03:00
|
|
|
useAfterRelease = new UseAfterRelease(this);
|
|
|
|
BR.Register(useAfterRelease);
|
|
|
|
|
|
|
|
releaseNotOwned = new BadRelease(this);
|
|
|
|
BR.Register(releaseNotOwned);
|
2009-02-05 02:49:09 +03:00
|
|
|
|
|
|
|
// First register "return" leaks.
|
|
|
|
const char* name = 0;
|
|
|
|
|
|
|
|
if (isGCEnabled())
|
|
|
|
name = "[naming convention] leak of returned object (GC)";
|
|
|
|
else if (getLangOptions().getGCMode() == LangOptions::HybridGC)
|
|
|
|
name = "[naming convention] leak of returned object (hybrid MM, "
|
|
|
|
"non-GC)";
|
|
|
|
else {
|
|
|
|
assert(getLangOptions().getGCMode() == LangOptions::NonGC);
|
|
|
|
name = "[naming convention] leak of returned object";
|
|
|
|
}
|
|
|
|
|
2009-02-05 09:50:21 +03:00
|
|
|
leakAtReturn = new LeakAtReturn(this, name);
|
|
|
|
BR.Register(leakAtReturn);
|
2008-04-18 07:39:05 +04:00
|
|
|
|
2009-02-05 02:49:09 +03:00
|
|
|
// Second, register leaks within a function/method.
|
|
|
|
if (isGCEnabled())
|
|
|
|
name = "leak (GC)";
|
|
|
|
else if (getLangOptions().getGCMode() == LangOptions::HybridGC)
|
|
|
|
name = "leak (hybrid MM, non-GC)";
|
|
|
|
else {
|
|
|
|
assert(getLangOptions().getGCMode() == LangOptions::NonGC);
|
|
|
|
name = "leak";
|
|
|
|
}
|
|
|
|
|
2009-02-05 09:50:21 +03:00
|
|
|
leakWithinFunction = new LeakWithinFunction(this, name);
|
|
|
|
BR.Register(leakWithinFunction);
|
|
|
|
|
|
|
|
// Save the reference to the BugReporter.
|
|
|
|
this->BR = &BR;
|
2009-02-05 02:49:09 +03:00
|
|
|
}
|
2008-05-01 03:47:44 +04:00
|
|
|
|
|
|
|
static const char* Msgs[] = {
|
|
|
|
"Code is compiled in garbage collection only mode" // GC only
|
|
|
|
" (the bug occurs with garbage collection enabled).",
|
|
|
|
|
|
|
|
"Code is compiled without garbage collection.", // No GC.
|
|
|
|
|
|
|
|
"Code is compiled for use with and without garbage collection (GC)."
|
|
|
|
" The bug occurs with GC enabled.", // Hybrid, with GC.
|
|
|
|
|
|
|
|
"Code is compiled for use with and without garbage collection (GC)."
|
|
|
|
" The bug occurs in non-GC mode." // Hyrbird, without GC/
|
|
|
|
};
|
|
|
|
|
|
|
|
std::pair<const char**,const char**> CFRefReport::getExtraDescriptiveText() {
|
|
|
|
CFRefCount& TF = static_cast<CFRefBug&>(getBugType()).getTF();
|
|
|
|
|
|
|
|
switch (TF.getLangOptions().getGCMode()) {
|
|
|
|
default:
|
|
|
|
assert(false);
|
2008-05-01 08:02:04 +04:00
|
|
|
|
|
|
|
case LangOptions::GCOnly:
|
|
|
|
assert (TF.isGCEnabled());
|
2008-08-12 22:30:56 +04:00
|
|
|
return std::make_pair(&Msgs[0], &Msgs[0]+1);
|
|
|
|
|
2008-05-01 03:47:44 +04:00
|
|
|
case LangOptions::NonGC:
|
|
|
|
assert (!TF.isGCEnabled());
|
|
|
|
return std::make_pair(&Msgs[1], &Msgs[1]+1);
|
|
|
|
|
|
|
|
case LangOptions::HybridGC:
|
|
|
|
if (TF.isGCEnabled())
|
|
|
|
return std::make_pair(&Msgs[2], &Msgs[2]+1);
|
|
|
|
else
|
|
|
|
return std::make_pair(&Msgs[3], &Msgs[3]+1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-19 00:57:45 +03:00
|
|
|
static inline bool contains(const llvm::SmallVectorImpl<ArgEffect>& V,
|
|
|
|
ArgEffect X) {
|
|
|
|
for (llvm::SmallVectorImpl<ArgEffect>::const_iterator I=V.begin(), E=V.end();
|
|
|
|
I!=E; ++I)
|
|
|
|
if (*I == X) return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-01-24 03:55:43 +03:00
|
|
|
PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode<GRState>* N,
|
|
|
|
const ExplodedNode<GRState>* PrevN,
|
|
|
|
const ExplodedGraph<GRState>& G,
|
2009-02-18 06:48:14 +03:00
|
|
|
BugReporter& BR,
|
|
|
|
NodeResolver& NR) {
|
2008-04-18 07:39:05 +04:00
|
|
|
|
2009-01-28 08:29:13 +03:00
|
|
|
// Check if the type state has changed.
|
|
|
|
GRStateManager &StMgr = cast<GRBugReporter>(BR).getStateManager();
|
|
|
|
GRStateRef PrevSt(PrevN->getState(), StMgr);
|
|
|
|
GRStateRef CurrSt(N->getState(), StMgr);
|
2009-01-28 08:06:46 +03:00
|
|
|
|
2009-01-28 08:29:13 +03:00
|
|
|
const RefVal* CurrT = CurrSt.get<RefBindings>(Sym);
|
|
|
|
if (!CurrT) return NULL;
|
|
|
|
|
|
|
|
const RefVal& CurrV = *CurrT;
|
|
|
|
const RefVal* PrevT = PrevSt.get<RefBindings>(Sym);
|
2008-05-05 21:53:17 +04:00
|
|
|
|
2009-02-19 00:57:45 +03:00
|
|
|
// Create a string buffer to constain all the useful things we want
|
|
|
|
// to tell the user.
|
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
|
|
|
|
2009-02-18 06:48:14 +03:00
|
|
|
// This is the allocation site since the previous node had no bindings
|
|
|
|
// for this symbol.
|
2008-04-18 08:55:01 +04:00
|
|
|
if (!PrevT) {
|
2008-05-05 21:53:17 +04:00
|
|
|
Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
|
|
|
|
|
2009-01-28 08:15:02 +03:00
|
|
|
if (CallExpr *CE = dyn_cast<CallExpr>(S)) {
|
|
|
|
// Get the name of the callee (if it is available).
|
|
|
|
SVal X = CurrSt.GetSVal(CE->getCallee());
|
|
|
|
if (loc::FuncVal* FV = dyn_cast<loc::FuncVal>(&X))
|
|
|
|
os << "Call to function '" << FV->getDecl()->getNameAsString() <<'\'';
|
|
|
|
else
|
2009-01-28 09:01:42 +03:00
|
|
|
os << "function call";
|
2009-01-28 08:15:02 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
assert (isa<ObjCMessageExpr>(S));
|
2009-01-28 09:01:42 +03:00
|
|
|
os << "Method";
|
2008-05-05 21:53:17 +04:00
|
|
|
}
|
2009-01-28 08:15:02 +03:00
|
|
|
|
2009-01-28 09:06:36 +03:00
|
|
|
if (CurrV.getObjKind() == RetEffect::CF) {
|
|
|
|
os << " returns a Core Foundation object with a ";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
assert (CurrV.getObjKind() == RetEffect::ObjC);
|
|
|
|
os << " returns an Objective-C object with a ";
|
|
|
|
}
|
2009-01-28 09:01:42 +03:00
|
|
|
|
2009-01-28 09:25:48 +03:00
|
|
|
if (CurrV.isOwned()) {
|
|
|
|
os << "+1 retain count (owning reference).";
|
|
|
|
|
|
|
|
if (static_cast<CFRefBug&>(getBugType()).getTF().isGCEnabled()) {
|
|
|
|
assert(CurrV.getObjKind() == RetEffect::CF);
|
|
|
|
os << " "
|
|
|
|
"Core Foundation objects are not automatically garbage collected.";
|
|
|
|
}
|
|
|
|
}
|
2008-04-18 08:55:01 +04:00
|
|
|
else {
|
|
|
|
assert (CurrV.isNotOwned());
|
2009-01-28 08:15:02 +03:00
|
|
|
os << "+0 retain count (non-owning reference).";
|
2008-04-18 08:55:01 +04:00
|
|
|
}
|
2008-05-05 21:53:17 +04:00
|
|
|
|
2008-04-18 08:55:01 +04:00
|
|
|
FullSourceLoc Pos(S->getLocStart(), BR.getContext().getSourceManager());
|
2009-01-28 07:47:13 +03:00
|
|
|
PathDiagnosticPiece* P = new PathDiagnosticPiece(Pos, os.str());
|
2008-04-18 08:55:01 +04:00
|
|
|
|
|
|
|
if (Expr* Exp = dyn_cast<Expr>(S))
|
|
|
|
P->addRange(Exp->getSourceRange());
|
|
|
|
|
|
|
|
return P;
|
|
|
|
}
|
|
|
|
|
2009-02-19 00:57:45 +03:00
|
|
|
// Gather up the effects that were performed on the object at this
|
|
|
|
// program point
|
|
|
|
llvm::SmallVector<ArgEffect, 2> AEffects;
|
|
|
|
|
2009-02-18 06:48:14 +03:00
|
|
|
if (const RetainSummary *Summ = TF.getSummaryOfNode(NR.getOriginalNode(N))) {
|
|
|
|
// We only have summaries attached to nodes after evaluating CallExpr and
|
|
|
|
// ObjCMessageExprs.
|
|
|
|
Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
|
|
|
|
|
|
|
|
if (CallExpr *CE = dyn_cast<CallExpr>(S)) {
|
|
|
|
// Iterate through the parameter expressions and see if the symbol
|
|
|
|
// was ever passed as an argument.
|
|
|
|
unsigned i = 0;
|
2008-04-18 08:55:01 +04:00
|
|
|
|
2009-02-18 06:48:14 +03:00
|
|
|
for (CallExpr::arg_iterator AI=CE->arg_begin(), AE=CE->arg_end();
|
|
|
|
AI!=AE; ++AI, ++i) {
|
2009-02-19 00:57:45 +03:00
|
|
|
|
2009-02-18 06:48:14 +03:00
|
|
|
// Retrieve the value of the arugment.
|
|
|
|
SVal X = CurrSt.GetSVal(*AI);
|
2009-02-19 00:57:45 +03:00
|
|
|
|
2009-02-18 06:48:14 +03:00
|
|
|
// Is it the symbol we're interested in?
|
|
|
|
if (!isa<loc::SymbolVal>(X) ||
|
|
|
|
Sym != cast<loc::SymbolVal>(X).getSymbol())
|
|
|
|
continue;
|
2008-04-18 09:32:44 +04:00
|
|
|
|
2009-02-18 06:48:14 +03:00
|
|
|
// We have an argument. Get the effect!
|
|
|
|
AEffects.push_back(Summ->getArg(i));
|
2008-04-18 09:32:44 +04:00
|
|
|
}
|
2009-02-18 06:48:14 +03:00
|
|
|
}
|
|
|
|
else if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
|
|
|
|
if (Expr *receiver = ME->getReceiver()) {
|
2009-02-19 00:57:45 +03:00
|
|
|
SVal RetV = CurrSt.GetSVal(receiver);
|
|
|
|
if (isa<loc::SymbolVal>(RetV) &&
|
|
|
|
Sym == cast<loc::SymbolVal>(RetV).getSymbol()) {
|
|
|
|
// The symbol we are tracking is the receiver.
|
|
|
|
AEffects.push_back(Summ->getReceiverEffect());
|
|
|
|
}
|
2009-02-18 06:48:14 +03:00
|
|
|
}
|
|
|
|
}
|
2009-02-19 00:57:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
// Get the previous type state.
|
|
|
|
RefVal PrevV = *PrevT;
|
|
|
|
|
|
|
|
// Specially handle CFMakeCollectable and friends.
|
|
|
|
if (contains(AEffects, MakeCollectable)) {
|
|
|
|
// Get the name of the function.
|
|
|
|
Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
|
|
|
|
loc::FuncVal FV =
|
|
|
|
cast<loc::FuncVal>(CurrSt.GetSVal(cast<CallExpr>(S)->getCallee()));
|
|
|
|
const std::string& FName = FV.getDecl()->getNameAsString();
|
2008-04-18 08:55:01 +04:00
|
|
|
|
2009-02-19 00:57:45 +03:00
|
|
|
if (TF.isGCEnabled()) {
|
|
|
|
// Determine if the object's reference count was pushed to zero.
|
|
|
|
assert(!(PrevV == CurrV) && "The typestate *must* have changed.");
|
|
|
|
|
|
|
|
os << "In GC mode a call to '" << FName
|
|
|
|
<< "' decrements an object's retain count and registers the "
|
|
|
|
"object with the garbage collector. ";
|
2009-02-18 06:48:14 +03:00
|
|
|
|
2009-02-19 01:57:22 +03:00
|
|
|
if (CurrV.getKind() == RefVal::Released) {
|
|
|
|
assert(CurrV.getCount() == 0);
|
|
|
|
os << "Since it now has a 0 retain count the object can be "
|
2009-02-19 00:57:45 +03:00
|
|
|
"automatically collected by the garbage collector.";
|
2009-02-19 01:57:22 +03:00
|
|
|
}
|
2009-02-19 00:57:45 +03:00
|
|
|
else
|
|
|
|
os << "An object must have a 0 retain count to be garbage collected. "
|
|
|
|
"After this call its retain count is +" << CurrV.getCount()
|
|
|
|
<< '.';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
os << "When GC is not enabled a call to '" << FName
|
|
|
|
<< "' has no effect on its argument.";
|
|
|
|
|
|
|
|
// Nothing more to say.
|
|
|
|
break;
|
2009-02-18 06:48:14 +03:00
|
|
|
}
|
|
|
|
|
2009-02-19 00:57:45 +03:00
|
|
|
// Determine if the typestate has changed.
|
|
|
|
if (!(PrevV == CurrV))
|
|
|
|
switch (CurrV.getKind()) {
|
2009-02-18 06:48:14 +03:00
|
|
|
case RefVal::Owned:
|
|
|
|
case RefVal::NotOwned:
|
|
|
|
|
|
|
|
if (PrevV.getCount() == CurrV.getCount())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (PrevV.getCount() > CurrV.getCount())
|
|
|
|
os << "Reference count decremented.";
|
|
|
|
else
|
|
|
|
os << "Reference count incremented.";
|
2009-02-19 01:57:22 +03:00
|
|
|
|
2009-02-18 06:48:14 +03:00
|
|
|
if (unsigned Count = CurrV.getCount()) {
|
2009-02-19 01:57:22 +03:00
|
|
|
os << " The object now has +" << Count;
|
2009-02-18 06:48:14 +03:00
|
|
|
|
|
|
|
if (Count > 1)
|
|
|
|
os << " retain counts.";
|
|
|
|
else
|
|
|
|
os << " retain count.";
|
|
|
|
}
|
2009-02-19 01:57:22 +03:00
|
|
|
|
|
|
|
if (PrevV.getKind() == RefVal::Released) {
|
|
|
|
assert(TF.isGCEnabled() && CurrV.getCount() > 0);
|
|
|
|
os << " The object is not eligible for garbage collection until the "
|
|
|
|
"retain count reaches 0 again.";
|
|
|
|
}
|
|
|
|
|
2009-02-18 06:48:14 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::Released:
|
|
|
|
os << "Object released.";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::ReturnedOwned:
|
|
|
|
os << "Object returned to caller as an owning reference (single retain "
|
|
|
|
"count transferred to caller).";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::ReturnedNotOwned:
|
|
|
|
os << "Object returned to caller with a +0 (non-owning) retain count.";
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return NULL;
|
2009-02-19 00:57:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Emit any remaining diagnostics for the argument effects (if any).
|
|
|
|
for (llvm::SmallVectorImpl<ArgEffect>::iterator I=AEffects.begin(),
|
|
|
|
E=AEffects.end(); I != E; ++I) {
|
|
|
|
|
|
|
|
// A bunch of things have alternate behavior under GC.
|
|
|
|
if (TF.isGCEnabled())
|
|
|
|
switch (*I) {
|
|
|
|
default: break;
|
|
|
|
case Autorelease:
|
|
|
|
os << "In GC mode an 'autorelease' has no effect.";
|
|
|
|
continue;
|
|
|
|
case IncRefMsg:
|
|
|
|
os << "In GC mode the 'retain' message has no effect.";
|
|
|
|
continue;
|
|
|
|
case DecRefMsg:
|
|
|
|
os << "In GC mode the 'release' message has no effect.";
|
|
|
|
continue;
|
|
|
|
}
|
2009-02-18 06:48:14 +03:00
|
|
|
}
|
2009-02-19 00:57:45 +03:00
|
|
|
} while(0);
|
2009-02-18 06:48:14 +03:00
|
|
|
|
|
|
|
if (os.str().empty())
|
|
|
|
return 0; // We have nothing to say!
|
2008-04-18 08:55:01 +04:00
|
|
|
|
|
|
|
Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
|
|
|
|
FullSourceLoc Pos(S->getLocStart(), BR.getContext().getSourceManager());
|
2009-01-28 07:47:13 +03:00
|
|
|
PathDiagnosticPiece* P = new PathDiagnosticPiece(Pos, os.str());
|
2008-04-18 08:55:01 +04:00
|
|
|
|
|
|
|
// Add the range by scanning the children of the statement for any bindings
|
|
|
|
// to Sym.
|
|
|
|
for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I)
|
|
|
|
if (Expr* Exp = dyn_cast_or_null<Expr>(*I)) {
|
2009-01-28 08:06:46 +03:00
|
|
|
SVal X = CurrSt.GetSVal(Exp);
|
2008-10-17 09:57:07 +04:00
|
|
|
if (loc::SymbolVal* SV = dyn_cast<loc::SymbolVal>(&X))
|
2009-02-19 01:17:20 +03:00
|
|
|
if (SV->getSymbol() == Sym) {
|
|
|
|
P->addRange(Exp->getSourceRange());
|
|
|
|
break;
|
|
|
|
}
|
2008-04-18 08:55:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return P;
|
2008-04-18 07:39:05 +04:00
|
|
|
}
|
|
|
|
|
2008-10-04 09:50:14 +04:00
|
|
|
namespace {
|
|
|
|
class VISIBILITY_HIDDEN FindUniqueBinding :
|
|
|
|
public StoreManager::BindingsHandler {
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef Sym;
|
2008-10-04 09:50:14 +04:00
|
|
|
MemRegion* Binding;
|
|
|
|
bool First;
|
|
|
|
|
|
|
|
public:
|
2008-12-05 05:27:51 +03:00
|
|
|
FindUniqueBinding(SymbolRef sym) : Sym(sym), Binding(0), First(true) {}
|
2008-10-04 09:50:14 +04:00
|
|
|
|
2008-10-17 09:57:07 +04:00
|
|
|
bool HandleBinding(StoreManager& SMgr, Store store, MemRegion* R, SVal val) {
|
|
|
|
if (const loc::SymbolVal* SV = dyn_cast<loc::SymbolVal>(&val)) {
|
2008-10-04 09:50:14 +04:00
|
|
|
if (SV->getSymbol() != Sym)
|
|
|
|
return true;
|
|
|
|
}
|
2008-10-17 09:57:07 +04:00
|
|
|
else if (const nonloc::SymbolVal* SV=dyn_cast<nonloc::SymbolVal>(&val)) {
|
2008-10-04 09:50:14 +04:00
|
|
|
if (SV->getSymbol() != Sym)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (Binding) {
|
|
|
|
First = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Binding = R;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
operator bool() { return First && Binding; }
|
|
|
|
MemRegion* getRegion() { return Binding; }
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2009-01-24 03:55:43 +03:00
|
|
|
static std::pair<const ExplodedNode<GRState>*,const MemRegion*>
|
2009-02-08 01:19:59 +03:00
|
|
|
GetAllocationSite(GRStateManager& StateMgr, const ExplodedNode<GRState>* N,
|
2008-12-05 05:27:51 +03:00
|
|
|
SymbolRef Sym) {
|
2008-05-02 03:13:35 +04:00
|
|
|
|
2008-08-29 04:47:32 +04:00
|
|
|
// Find both first node that referred to the tracked symbol and the
|
|
|
|
// memory location that value was store to.
|
2009-01-24 03:55:43 +03:00
|
|
|
const ExplodedNode<GRState>* Last = N;
|
|
|
|
const MemRegion* FirstBinding = 0;
|
2008-05-16 22:33:44 +04:00
|
|
|
|
2008-05-02 03:13:35 +04:00
|
|
|
while (N) {
|
2008-08-13 08:27:00 +04:00
|
|
|
const GRState* St = N->getState();
|
2008-08-15 01:16:54 +04:00
|
|
|
RefBindings B = St->get<RefBindings>();
|
2008-05-03 00:53:50 +04:00
|
|
|
|
2008-07-07 20:21:19 +04:00
|
|
|
if (!B.lookup(Sym))
|
2008-05-02 03:13:35 +04:00
|
|
|
break;
|
2008-08-29 04:47:32 +04:00
|
|
|
|
2009-02-08 01:19:59 +03:00
|
|
|
FindUniqueBinding FB(Sym);
|
|
|
|
StateMgr.iterBindings(St, FB);
|
|
|
|
if (FB) FirstBinding = FB.getRegion();
|
2008-05-03 00:53:50 +04:00
|
|
|
|
2008-05-02 03:13:35 +04:00
|
|
|
Last = N;
|
|
|
|
N = N->pred_empty() ? NULL : *(N->pred_begin());
|
|
|
|
}
|
|
|
|
|
2008-08-29 04:47:32 +04:00
|
|
|
return std::make_pair(Last, FirstBinding);
|
2008-05-16 22:33:44 +04:00
|
|
|
}
|
|
|
|
|
2009-01-24 03:55:43 +03:00
|
|
|
PathDiagnosticPiece*
|
|
|
|
CFRefReport::getEndPath(BugReporter& br, const ExplodedNode<GRState>* EndN) {
|
2008-05-23 03:45:19 +04:00
|
|
|
|
2008-08-29 04:47:32 +04:00
|
|
|
GRBugReporter& BR = cast<GRBugReporter>(br);
|
2008-05-23 03:45:19 +04:00
|
|
|
// Tell the BugReporter to report cases when the tracked symbol is
|
|
|
|
// assigned to different variables, etc.
|
2008-07-03 01:24:01 +04:00
|
|
|
cast<GRBugReporter>(BR).addNotableSymbol(Sym);
|
2009-02-08 01:04:05 +03:00
|
|
|
return RangedBugReport::getEndPath(BR, EndN);
|
|
|
|
}
|
|
|
|
|
|
|
|
PathDiagnosticPiece*
|
|
|
|
CFRefLeakReport::getEndPath(BugReporter& br, const ExplodedNode<GRState>* EndN){
|
|
|
|
|
|
|
|
GRBugReporter& BR = cast<GRBugReporter>(br);
|
|
|
|
// Tell the BugReporter to report cases when the tracked symbol is
|
|
|
|
// assigned to different variables, etc.
|
|
|
|
cast<GRBugReporter>(BR).addNotableSymbol(Sym);
|
|
|
|
|
|
|
|
// We are reporting a leak. Walk up the graph to get to the first node where
|
|
|
|
// the symbol appeared, and also get the first VarDecl that tracked object
|
2008-05-16 22:33:44 +04:00
|
|
|
// is stored to.
|
2009-01-24 03:55:43 +03:00
|
|
|
const ExplodedNode<GRState>* AllocNode = 0;
|
|
|
|
const MemRegion* FirstBinding = 0;
|
2008-08-29 04:47:32 +04:00
|
|
|
|
|
|
|
llvm::tie(AllocNode, FirstBinding) =
|
2009-02-08 01:19:59 +03:00
|
|
|
GetAllocationSite(BR.getStateManager(), EndN, Sym);
|
2008-05-16 22:33:44 +04:00
|
|
|
|
|
|
|
// Get the allocate site.
|
|
|
|
assert (AllocNode);
|
|
|
|
Stmt* FirstStmt = cast<PostStmt>(AllocNode->getLocation()).getStmt();
|
2008-05-02 03:13:35 +04:00
|
|
|
|
2008-05-05 22:50:19 +04:00
|
|
|
SourceManager& SMgr = BR.getContext().getSourceManager();
|
2009-01-16 10:36:28 +03:00
|
|
|
unsigned AllocLine =SMgr.getInstantiationLineNumber(FirstStmt->getLocStart());
|
2008-05-05 22:50:19 +04:00
|
|
|
|
2009-02-19 02:28:26 +03:00
|
|
|
// Get the leak site. We want to find the last place where the symbol
|
|
|
|
// was used in an expression.
|
|
|
|
const ExplodedNode<GRState>* LeakN = EndN;
|
|
|
|
Stmt *S = 0;
|
|
|
|
|
|
|
|
while (LeakN) {
|
|
|
|
ProgramPoint P = LeakN->getLocation();
|
|
|
|
|
|
|
|
if (const PostStmt *PS = dyn_cast<PostStmt>(&P))
|
|
|
|
S = PS->getStmt();
|
|
|
|
else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P))
|
|
|
|
S = BE->getSrc()->getTerminator();
|
|
|
|
|
|
|
|
if (S) {
|
|
|
|
// Scan 'S' for uses of Sym.
|
|
|
|
GRStateRef state(LeakN->getState(), BR.getStateManager());
|
|
|
|
bool foundSymbol = false;
|
2009-02-19 21:18:48 +03:00
|
|
|
|
|
|
|
// First check if 'S' itself binds to the symbol.
|
|
|
|
if (Expr *Ex = dyn_cast<Expr>(S)) {
|
|
|
|
SVal X = state.GetSVal(Ex);
|
|
|
|
if (isa<loc::SymbolVal>(X) &&
|
|
|
|
cast<loc::SymbolVal>(X).getSymbol() == Sym)
|
|
|
|
foundSymbol = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!foundSymbol)
|
|
|
|
for (Stmt::child_iterator I=S->child_begin(), E=S->child_end();
|
|
|
|
I!=E; ++I)
|
|
|
|
if (Expr *Ex = dyn_cast_or_null<Expr>(*I)) {
|
|
|
|
SVal X = state.GetSVal(Ex);
|
|
|
|
if (isa<loc::SymbolVal>(X) &&
|
|
|
|
cast<loc::SymbolVal>(X).getSymbol() == Sym){
|
|
|
|
foundSymbol = true;
|
|
|
|
break;
|
|
|
|
}
|
2009-02-19 02:28:26 +03:00
|
|
|
}
|
2009-02-19 21:18:48 +03:00
|
|
|
|
2009-02-19 02:28:26 +03:00
|
|
|
if (foundSymbol)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
LeakN = LeakN->pred_empty() ? 0 : *(LeakN->pred_begin());
|
|
|
|
}
|
2008-05-05 22:50:19 +04:00
|
|
|
|
2009-02-19 02:28:26 +03:00
|
|
|
assert(LeakN && S && "No leak site found.");
|
2008-05-05 22:50:19 +04:00
|
|
|
|
|
|
|
// Generate the diagnostic.
|
2009-02-19 01:59:04 +03:00
|
|
|
FullSourceLoc L(S->getLocStart(), SMgr);
|
2009-02-08 00:59:45 +03:00
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2008-05-03 00:53:50 +04:00
|
|
|
|
2008-05-05 22:50:19 +04:00
|
|
|
os << "Object allocated on line " << AllocLine;
|
2008-05-03 00:53:50 +04:00
|
|
|
|
2008-08-29 04:47:32 +04:00
|
|
|
if (FirstBinding)
|
2008-10-04 09:50:14 +04:00
|
|
|
os << " and stored into '" << FirstBinding->getString() << '\'';
|
2008-10-23 03:56:21 +04:00
|
|
|
|
|
|
|
// Get the retain count.
|
|
|
|
const RefVal* RV = EndN->getState()->get<RefBindings>(Sym);
|
2008-10-04 09:50:14 +04:00
|
|
|
|
2008-10-23 03:56:21 +04:00
|
|
|
if (RV->getKind() == RefVal::ErrorLeakReturned) {
|
2008-12-02 04:26:07 +03:00
|
|
|
// FIXME: Per comments in rdar://6320065, "create" only applies to CF
|
|
|
|
// ojbects. Only "copy", "alloc", "retain" and "new" transfer ownership
|
|
|
|
// to the caller for NS objects.
|
2008-10-23 03:56:21 +04:00
|
|
|
ObjCMethodDecl& MD = cast<ObjCMethodDecl>(BR.getGraph().getCodeDecl());
|
|
|
|
os << " is returned from a method whose name ('"
|
2008-11-24 06:33:13 +03:00
|
|
|
<< MD.getSelector().getAsString()
|
2009-01-07 03:39:56 +03:00
|
|
|
<< "') does not contain 'copy' or otherwise starts with"
|
2008-10-25 01:22:44 +04:00
|
|
|
" 'new' or 'alloc'. This violates the naming convention rules given"
|
2008-10-23 03:56:21 +04:00
|
|
|
" in the Memory Management Guide for Cocoa (object leaked).";
|
|
|
|
}
|
|
|
|
else
|
2008-10-25 01:22:44 +04:00
|
|
|
os << " is no longer referenced after this point and has a retain count of"
|
|
|
|
" +"
|
2008-10-23 03:56:21 +04:00
|
|
|
<< RV->getCount() << " (object leaked).";
|
2008-05-02 03:13:35 +04:00
|
|
|
|
2009-02-19 01:59:04 +03:00
|
|
|
return new PathDiagnosticPiece(L, os.str());
|
2008-05-02 03:13:35 +04:00
|
|
|
}
|
|
|
|
|
2008-04-18 03:43:50 +04:00
|
|
|
|
2009-02-18 06:48:14 +03:00
|
|
|
CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf,
|
|
|
|
ExplodedNode<GRState> *n,
|
2009-02-08 01:38:00 +03:00
|
|
|
SymbolRef sym, GRExprEngine& Eng)
|
2009-02-18 06:48:14 +03:00
|
|
|
: CFRefReport(D, tf, n, sym)
|
2009-02-08 01:19:59 +03:00
|
|
|
{
|
|
|
|
|
2008-05-16 22:33:44 +04:00
|
|
|
// Most bug reports are cached at the location where they occured.
|
|
|
|
// With leaks, we want to unique them by the location where they were
|
2009-02-08 01:19:59 +03:00
|
|
|
// allocated, and only report a single path. To do this, we need to find
|
|
|
|
// the allocation site of a piece of tracked memory, which we do via a
|
|
|
|
// call to GetAllocationSite. This will walk the ExplodedGraph backwards.
|
|
|
|
// Note that this is *not* the trimmed graph; we are guaranteed, however,
|
|
|
|
// that all ancestor nodes that represent the allocation site have the
|
|
|
|
// same SourceLocation.
|
|
|
|
const ExplodedNode<GRState>* AllocNode = 0;
|
|
|
|
|
|
|
|
llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding.
|
2009-02-08 01:38:00 +03:00
|
|
|
GetAllocationSite(Eng.getStateManager(), getEndNode(), getSymbol());
|
2009-02-08 01:19:59 +03:00
|
|
|
|
|
|
|
// Get the SourceLocation for the allocation site.
|
2009-02-08 01:38:00 +03:00
|
|
|
ProgramPoint P = AllocNode->getLocation();
|
2009-02-08 01:19:59 +03:00
|
|
|
AllocSite = cast<PostStmt>(P).getStmt()->getLocStart();
|
2009-02-08 01:38:00 +03:00
|
|
|
|
|
|
|
// Fill in the description of the bug.
|
|
|
|
Description.clear();
|
|
|
|
llvm::raw_string_ostream os(Description);
|
|
|
|
SourceManager& SMgr = Eng.getContext().getSourceManager();
|
|
|
|
unsigned AllocLine = SMgr.getInstantiationLineNumber(AllocSite);
|
2009-02-08 01:54:59 +03:00
|
|
|
os << "Potential leak of object allocated on line " << AllocLine;
|
|
|
|
|
|
|
|
// FIXME: AllocBinding doesn't get populated for RegionStore yet.
|
|
|
|
if (AllocBinding)
|
|
|
|
os << " and store into '" << AllocBinding->getString() << '\'';
|
2008-05-16 22:33:44 +04:00
|
|
|
}
|
|
|
|
|
2009-02-05 09:50:21 +03:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Handle dead symbols and end-of-path.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
void CFRefCount::EvalEndPath(GRExprEngine& Eng,
|
|
|
|
GREndPathNodeBuilder<GRState>& Builder) {
|
|
|
|
|
|
|
|
const GRState* St = Builder.getState();
|
|
|
|
RefBindings B = St->get<RefBindings>();
|
|
|
|
|
|
|
|
llvm::SmallVector<std::pair<SymbolRef, bool>, 10> Leaked;
|
|
|
|
const Decl* CodeDecl = &Eng.getGraph().getCodeDecl();
|
|
|
|
|
|
|
|
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
|
|
|
|
bool hasLeak = false;
|
|
|
|
|
|
|
|
std::pair<GRStateRef, bool> X =
|
|
|
|
HandleSymbolDeath(Eng.getStateManager(), St, CodeDecl,
|
|
|
|
(*I).first, (*I).second, hasLeak);
|
|
|
|
|
|
|
|
St = X.first;
|
|
|
|
if (hasLeak) Leaked.push_back(std::make_pair((*I).first, X.second));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Leaked.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
ExplodedNode<GRState>* N = Builder.MakeNode(St);
|
|
|
|
|
|
|
|
if (!N)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (llvm::SmallVector<std::pair<SymbolRef,bool>, 10>::iterator
|
|
|
|
I = Leaked.begin(), E = Leaked.end(); I != E; ++I) {
|
|
|
|
|
|
|
|
CFRefBug *BT = static_cast<CFRefBug*>(I->second ? leakAtReturn
|
|
|
|
: leakWithinFunction);
|
|
|
|
assert(BT && "BugType not initialized.");
|
2009-02-18 06:48:14 +03:00
|
|
|
CFRefLeakReport* report = new CFRefLeakReport(*BT, *this, N, I->first, Eng);
|
2009-02-05 09:50:21 +03:00
|
|
|
BR->EmitReport(report);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CFRefCount::EvalDeadSymbols(ExplodedNodeSet<GRState>& Dst,
|
|
|
|
GRExprEngine& Eng,
|
|
|
|
GRStmtNodeBuilder<GRState>& Builder,
|
|
|
|
ExplodedNode<GRState>* Pred,
|
|
|
|
Stmt* S,
|
|
|
|
const GRState* St,
|
|
|
|
SymbolReaper& SymReaper) {
|
|
|
|
|
2009-02-20 02:47:02 +03:00
|
|
|
// FIXME: a lot of copy-and-paste from EvalEndPath. Refactor.
|
2009-02-05 09:50:21 +03:00
|
|
|
RefBindings B = St->get<RefBindings>();
|
|
|
|
llvm::SmallVector<std::pair<SymbolRef,bool>, 10> Leaked;
|
|
|
|
|
|
|
|
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
|
|
|
|
E = SymReaper.dead_end(); I != E; ++I) {
|
|
|
|
|
|
|
|
const RefVal* T = B.lookup(*I);
|
|
|
|
if (!T) continue;
|
|
|
|
|
|
|
|
bool hasLeak = false;
|
|
|
|
|
|
|
|
std::pair<GRStateRef, bool> X
|
2009-02-20 02:47:02 +03:00
|
|
|
= HandleSymbolDeath(Eng.getStateManager(), St, 0, *I, *T, hasLeak);
|
2009-02-05 09:50:21 +03:00
|
|
|
|
|
|
|
St = X.first;
|
|
|
|
|
|
|
|
if (hasLeak)
|
|
|
|
Leaked.push_back(std::make_pair(*I,X.second));
|
|
|
|
}
|
|
|
|
|
2009-02-20 02:47:02 +03:00
|
|
|
if (!Leaked.empty()) {
|
|
|
|
// Create a new intermediate node representing the leak point. We
|
|
|
|
// use a special program point that represents this checker-specific
|
|
|
|
// transition. We use the address of RefBIndex as a unique tag for this
|
|
|
|
// checker. We will create another node (if we don't cache out) that
|
|
|
|
// removes the retain-count bindings from the state.
|
|
|
|
// NOTE: We use 'generateNode' so that it does interplay with the
|
|
|
|
// auto-transition logic.
|
|
|
|
ExplodedNode<GRState>* N =
|
|
|
|
Builder.generateNode(PostStmtCustom(S, &LeakProgramPointTag), St, Pred);
|
2009-02-05 09:50:21 +03:00
|
|
|
|
2009-02-20 02:47:02 +03:00
|
|
|
if (!N)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Generate the bug reports.
|
|
|
|
for (llvm::SmallVectorImpl<std::pair<SymbolRef,bool> >::iterator
|
|
|
|
I = Leaked.begin(), E = Leaked.end(); I != E; ++I) {
|
|
|
|
|
|
|
|
CFRefBug *BT = static_cast<CFRefBug*>(I->second ? leakAtReturn
|
|
|
|
: leakWithinFunction);
|
|
|
|
assert(BT && "BugType not initialized.");
|
|
|
|
CFRefLeakReport* report = new CFRefLeakReport(*BT, *this, N, I->first, Eng);
|
|
|
|
BR->EmitReport(report);
|
|
|
|
}
|
2009-02-05 09:50:21 +03:00
|
|
|
|
2009-02-20 02:47:02 +03:00
|
|
|
Pred = N;
|
2009-02-05 09:50:21 +03:00
|
|
|
}
|
2009-02-20 02:47:02 +03:00
|
|
|
|
|
|
|
// Now generate a new node that nukes the old bindings.
|
|
|
|
GRStateRef state(St, Eng.getStateManager());
|
|
|
|
RefBindings::Factory& F = state.get_context<RefBindings>();
|
|
|
|
|
|
|
|
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
|
|
|
|
E = SymReaper.dead_end(); I!=E; ++I)
|
|
|
|
B = F.Remove(B, *I);
|
|
|
|
|
|
|
|
state = state.set<RefBindings>(B);
|
|
|
|
Builder.MakeNode(Dst, S, Pred, state);
|
2009-02-05 09:50:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void CFRefCount::ProcessNonLeakError(ExplodedNodeSet<GRState>& Dst,
|
|
|
|
GRStmtNodeBuilder<GRState>& Builder,
|
|
|
|
Expr* NodeExpr, Expr* ErrorExpr,
|
|
|
|
ExplodedNode<GRState>* Pred,
|
|
|
|
const GRState* St,
|
|
|
|
RefVal::Kind hasErr, SymbolRef Sym) {
|
|
|
|
Builder.BuildSinks = true;
|
|
|
|
GRExprEngine::NodeTy* N = Builder.MakeNode(Dst, NodeExpr, Pred, St);
|
|
|
|
|
|
|
|
if (!N) return;
|
|
|
|
|
|
|
|
CFRefBug *BT = 0;
|
|
|
|
|
|
|
|
if (hasErr == RefVal::ErrorUseAfterRelease)
|
|
|
|
BT = static_cast<CFRefBug*>(useAfterRelease);
|
|
|
|
else {
|
|
|
|
assert(hasErr == RefVal::ErrorReleaseNotOwned);
|
|
|
|
BT = static_cast<CFRefBug*>(releaseNotOwned);
|
|
|
|
}
|
|
|
|
|
2009-02-18 06:48:14 +03:00
|
|
|
CFRefReport *report = new CFRefReport(*BT, *this, N, Sym);
|
2009-02-05 09:50:21 +03:00
|
|
|
report->addRange(ErrorExpr->getSourceRange());
|
|
|
|
BR->EmitReport(report);
|
|
|
|
}
|
|
|
|
|
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-05-01 03:47:44 +04:00
|
|
|
GRTransferFuncs* clang::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled,
|
|
|
|
const LangOptions& lopts) {
|
2008-07-22 20:21:24 +04:00
|
|
|
return new CFRefCount(Ctx, GCEnabled, lopts);
|
2008-04-11 02:58:08 +04:00
|
|
|
}
|