2011-09-02 14:44:22 +08:00
|
|
|
//==-- RetainCountChecker.cpp - Checks for leaks and other issues -*- C++ -*--//
|
2008-03-06 08:08:09 +08:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2011-09-02 14:44:22 +08:00
|
|
|
// This file defines the methods for RetainCountChecker, which implements
|
|
|
|
// a reference count checker for Core Foundation and Cocoa on (Mac OS X).
|
2008-03-06 08:08:09 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
#include "ClangSACheckers.h"
|
2010-02-18 08:05:58 +08:00
|
|
|
#include "clang/AST/DeclObjC.h"
|
2011-03-31 01:41:19 +08:00
|
|
|
#include "clang/AST/DeclCXX.h"
|
2008-05-01 07:47:44 +08:00
|
|
|
#include "clang/Basic/LangOptions.h"
|
2008-05-02 07:13:35 +08:00
|
|
|
#include "clang/Basic/SourceManager.h"
|
2011-09-02 14:44:22 +08:00
|
|
|
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
|
2011-11-15 05:59:21 +08:00
|
|
|
#include "clang/AST/ParentMap.h"
|
2011-09-02 14:44:22 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
2011-02-10 09:03:03 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
|
2011-09-02 14:44:22 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
2011-08-16 06:09:50 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
|
2011-02-10 09:03:03 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
|
2011-09-02 16:02:59 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
|
2008-03-11 14:39:11 +08:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "llvm/ADT/FoldingSet.h"
|
2008-10-21 23:53:15 +08:00
|
|
|
#include "llvm/ADT/ImmutableList.h"
|
2010-02-18 08:05:58 +08:00
|
|
|
#include "llvm/ADT/ImmutableMap.h"
|
2012-02-04 21:45:25 +08:00
|
|
|
#include "llvm/ADT/SmallString.h"
|
2008-05-17 02:33:44 +08:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2010-02-18 08:05:58 +08:00
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2011-07-23 18:55:15 +08:00
|
|
|
#include <cstdarg>
|
2008-03-06 08:08:09 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
2010-12-23 15:20:52 +08:00
|
|
|
using namespace ento;
|
2010-01-27 14:13:48 +08:00
|
|
|
using llvm::StrInStrNoCase;
|
2008-11-06 00:54:44 +08:00
|
|
|
|
2009-05-09 07:09:42 +08:00
|
|
|
namespace {
|
2011-09-02 14:44:22 +08:00
|
|
|
/// Wrapper around different kinds of node builder, so that helper functions
|
|
|
|
/// can have a common interface.
|
2011-01-11 18:41:37 +08:00
|
|
|
class GenericNodeBuilderRefCount {
|
2011-10-06 07:44:11 +08:00
|
|
|
CheckerContext *C;
|
2011-08-13 07:04:46 +08:00
|
|
|
const ProgramPointTag *tag;
|
2009-05-09 07:09:42 +08:00
|
|
|
public:
|
2011-10-06 07:44:11 +08:00
|
|
|
GenericNodeBuilderRefCount(CheckerContext &c,
|
2011-10-26 03:56:48 +08:00
|
|
|
const ProgramPointTag *t = 0)
|
2011-10-26 03:57:11 +08:00
|
|
|
: C(&c), tag(t){}
|
2009-08-06 20:48:26 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ExplodedNode *MakeNode(ProgramStateRef state, ExplodedNode *Pred,
|
2011-10-19 07:05:58 +08:00
|
|
|
bool MarkAsSink = false) {
|
2011-10-27 05:06:34 +08:00
|
|
|
return C->addTransition(state, Pred, tag, MarkAsSink);
|
2009-05-09 07:09:42 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2008-04-10 07:49:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-06-26 05:21:56 +08:00
|
|
|
// Primitives used for constructing summaries for function/method calls.
|
2008-04-10 07:49:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// ArgEffect is used to summarize a function/method call's effect on a
|
|
|
|
/// particular argument.
|
2011-08-25 03:10:50 +08:00
|
|
|
enum ArgEffect { DoNothing, Autorelease, Dealloc, DecRef, DecRefMsg,
|
2011-06-16 07:02:42 +08:00
|
|
|
DecRefBridgedTransfered,
|
2011-08-25 03:10:50 +08:00
|
|
|
IncRefMsg, IncRef, MakeCollectable, MayEscape,
|
2009-03-18 03:42:23 +08:00
|
|
|
NewAutoreleasePool, SelfOwn, StopTracking };
|
2008-06-26 05:21:56 +08:00
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
namespace llvm {
|
2009-05-03 13:20:50 +08:00
|
|
|
template <> struct FoldingSetTrait<ArgEffect> {
|
|
|
|
static inline void Profile(const ArgEffect X, FoldingSetNodeID& ID) {
|
|
|
|
ID.AddInteger((unsigned) X);
|
|
|
|
}
|
2008-06-26 05:21:56 +08:00
|
|
|
};
|
2008-03-11 14:39:11 +08:00
|
|
|
} // end llvm namespace
|
|
|
|
|
2009-05-03 13:20:50 +08:00
|
|
|
/// ArgEffects summarizes the effects of a function/method call on all of
|
|
|
|
/// its arguments.
|
|
|
|
typedef llvm::ImmutableMap<unsigned,ArgEffect> ArgEffects;
|
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
namespace {
|
2008-06-26 05:21:56 +08:00
|
|
|
|
|
|
|
/// RetEffect is used to summarize a function/method call's behavior with
|
2009-09-09 23:08:12 +08:00
|
|
|
/// respect to its return value.
|
2009-11-28 14:07:30 +08:00
|
|
|
class RetEffect {
|
2008-03-11 14:39:11 +08:00
|
|
|
public:
|
2011-08-22 05:58:18 +08:00
|
|
|
enum Kind { NoRet, OwnedSymbol, OwnedAllocatedSymbol,
|
2011-06-16 07:02:42 +08:00
|
|
|
NotOwnedSymbol, GCNotOwnedSymbol, ARCNotOwnedSymbol,
|
2009-05-13 04:06:54 +08:00
|
|
|
OwnedWhenTrackedReceiver };
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
enum ObjKind { CF, ObjC, AnyObj };
|
2009-01-28 13:56:51 +08:00
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
private:
|
2009-01-28 13:56:51 +08:00
|
|
|
Kind K;
|
|
|
|
ObjKind O;
|
|
|
|
|
2011-08-22 05:58:18 +08:00
|
|
|
RetEffect(Kind k, ObjKind o = AnyObj) : K(k), O(o) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
public:
|
2009-01-28 13:56:51 +08:00
|
|
|
Kind getKind() const { return K; }
|
|
|
|
|
|
|
|
ObjKind getObjKind() const { return O; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 07:03:22 +08:00
|
|
|
bool isOwned() const {
|
2009-05-13 04:06:54 +08:00
|
|
|
return K == OwnedSymbol || K == OwnedAllocatedSymbol ||
|
|
|
|
K == OwnedWhenTrackedReceiver;
|
2009-04-30 07:03:22 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-23 12:27:15 +08:00
|
|
|
bool operator==(const RetEffect &Other) const {
|
|
|
|
return K == Other.K && O == Other.O;
|
|
|
|
}
|
|
|
|
|
2009-05-13 04:06:54 +08:00
|
|
|
static RetEffect MakeOwnedWhenTrackedReceiver() {
|
|
|
|
return RetEffect(OwnedWhenTrackedReceiver, ObjC);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-01-28 13:56:51 +08:00
|
|
|
static RetEffect MakeOwned(ObjKind o, bool isAllocated = false) {
|
|
|
|
return RetEffect(isAllocated ? OwnedAllocatedSymbol : OwnedSymbol, o);
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-01-28 13:56:51 +08:00
|
|
|
static RetEffect MakeNotOwned(ObjKind o) {
|
|
|
|
return RetEffect(NotOwnedSymbol, o);
|
2009-04-28 03:14:45 +08:00
|
|
|
}
|
|
|
|
static RetEffect MakeGCNotOwned() {
|
|
|
|
return RetEffect(GCNotOwnedSymbol, ObjC);
|
|
|
|
}
|
2011-06-16 07:02:42 +08:00
|
|
|
static RetEffect MakeARCNotOwned() {
|
|
|
|
return RetEffect(ARCNotOwnedSymbol, ObjC);
|
|
|
|
}
|
2008-06-26 05:21:56 +08:00
|
|
|
static RetEffect MakeNoRet() {
|
|
|
|
return RetEffect(NoRet);
|
2008-06-24 02:02:52 +08:00
|
|
|
}
|
2012-03-18 09:26:10 +08:00
|
|
|
|
|
|
|
void Profile(llvm::FoldingSetNodeID& ID) const {
|
|
|
|
ID.AddInteger((unsigned) K);
|
|
|
|
ID.AddInteger((unsigned) O);
|
|
|
|
}
|
2008-03-11 14:39:11 +08:00
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Reference-counting logic (typestate + counts).
|
|
|
|
//===----------------------------------------------------------------------===//
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class RefVal {
|
2009-11-13 09:54:21 +08: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.
|
|
|
|
ERROR_START,
|
|
|
|
ErrorDeallocNotOwned, // -dealloc called on non-owned object.
|
|
|
|
ErrorDeallocGC, // Calling -dealloc with GC enabled.
|
|
|
|
ErrorUseAfterRelease, // Object used after released.
|
|
|
|
ErrorReleaseNotOwned, // Release of an object that was not owned.
|
|
|
|
ERROR_LEAK_START,
|
|
|
|
ErrorLeak, // A memory leak due to excessive reference counts.
|
|
|
|
ErrorLeakReturned, // A memory leak due to the returning method not having
|
|
|
|
// the correct naming conventions.
|
|
|
|
ErrorGCLeakReturned,
|
|
|
|
ErrorOverAutorelease,
|
|
|
|
ErrorReturnedNotOwned
|
|
|
|
};
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
private:
|
|
|
|
Kind kind;
|
|
|
|
RetEffect::ObjKind okind;
|
|
|
|
unsigned Cnt;
|
|
|
|
unsigned ACnt;
|
|
|
|
QualType T;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t)
|
|
|
|
: kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
public:
|
|
|
|
Kind getKind() const { return kind; }
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RetEffect::ObjKind getObjKind() const { return okind; }
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
unsigned getCount() const { return Cnt; }
|
|
|
|
unsigned getAutoreleaseCount() const { return ACnt; }
|
|
|
|
unsigned getCombinedCounts() const { return Cnt + ACnt; }
|
|
|
|
void clearCounts() { Cnt = 0; ACnt = 0; }
|
|
|
|
void setCount(unsigned i) { Cnt = i; }
|
|
|
|
void setAutoreleaseCount(unsigned i) { ACnt = i; }
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
QualType getType() const { return T; }
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
bool isOwned() const {
|
|
|
|
return getKind() == Owned;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
bool isNotOwned() const {
|
|
|
|
return getKind() == NotOwned;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
bool isReturnedOwned() const {
|
|
|
|
return getKind() == ReturnedOwned;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
bool isReturnedNotOwned() const {
|
|
|
|
return getKind() == ReturnedNotOwned;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
static RefVal makeOwned(RetEffect::ObjKind o, QualType t,
|
|
|
|
unsigned Count = 1) {
|
|
|
|
return RefVal(Owned, o, Count, 0, t);
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t,
|
|
|
|
unsigned Count = 0) {
|
|
|
|
return RefVal(NotOwned, o, Count, 0, t);
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
// Comparison, profiling, and pretty-printing.
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
bool operator==(const RefVal& X) const {
|
|
|
|
return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RefVal operator-(size_t i) const {
|
|
|
|
return RefVal(getKind(), getObjKind(), getCount() - i,
|
|
|
|
getAutoreleaseCount(), getType());
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RefVal operator+(size_t i) const {
|
|
|
|
return RefVal(getKind(), getObjKind(), getCount() + i,
|
|
|
|
getAutoreleaseCount(), getType());
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RefVal operator^(Kind k) const {
|
|
|
|
return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(),
|
|
|
|
getType());
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
RefVal autorelease() const {
|
|
|
|
return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1,
|
|
|
|
getType());
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
void Profile(llvm::FoldingSetNodeID& ID) const {
|
|
|
|
ID.AddInteger((unsigned) kind);
|
|
|
|
ID.AddInteger(Cnt);
|
|
|
|
ID.AddInteger(ACnt);
|
|
|
|
ID.Add(T);
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
void print(raw_ostream &Out) const;
|
2009-11-13 09:54:21 +08:00
|
|
|
};
|
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
void RefVal::print(raw_ostream &Out) const {
|
2009-11-13 09:54:21 +08:00
|
|
|
if (!T.isNull())
|
2011-08-29 03:11:56 +08:00
|
|
|
Out << "Tracked " << T.getAsString() << '/';
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
switch (getKind()) {
|
2011-09-02 14:44:22 +08:00
|
|
|
default: llvm_unreachable("Invalid RefVal kind");
|
2009-11-13 09:54:21 +08:00
|
|
|
case Owned: {
|
|
|
|
Out << "Owned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case NotOwned: {
|
|
|
|
Out << "NotOwned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ReturnedOwned: {
|
|
|
|
Out << "ReturnedOwned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ReturnedNotOwned: {
|
|
|
|
Out << "ReturnedNotOwned";
|
|
|
|
unsigned cnt = getCount();
|
|
|
|
if (cnt) Out << " (+ " << cnt << ")";
|
|
|
|
break;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case Released:
|
|
|
|
Out << "Released";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorDeallocGC:
|
|
|
|
Out << "-dealloc (GC)";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorDeallocNotOwned:
|
|
|
|
Out << "-dealloc (not-owned)";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorLeak:
|
|
|
|
Out << "Leaked";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorLeakReturned:
|
|
|
|
Out << "Leaked (Bad naming)";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorGCLeakReturned:
|
|
|
|
Out << "Leaked (GC-ed at return)";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorUseAfterRelease:
|
|
|
|
Out << "Use-After-Release [ERROR]";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case ErrorReleaseNotOwned:
|
|
|
|
Out << "Release of Not-Owned [ERROR]";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case RefVal::ErrorOverAutorelease:
|
|
|
|
Out << "Over autoreleased";
|
|
|
|
break;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
case RefVal::ErrorReturnedNotOwned:
|
|
|
|
Out << "Non-owned object returned instead of owned";
|
|
|
|
break;
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
if (ACnt) {
|
|
|
|
Out << " [ARC +" << ACnt << ']';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} //end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// RefBindings - State used to track object reference counts.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
typedef llvm::ImmutableMap<SymbolRef, RefVal> RefBindings;
|
|
|
|
|
|
|
|
namespace clang {
|
2010-12-23 15:20:52 +08:00
|
|
|
namespace ento {
|
2011-08-16 06:09:50 +08:00
|
|
|
template<>
|
|
|
|
struct ProgramStateTrait<RefBindings>
|
|
|
|
: public ProgramStatePartialTrait<RefBindings> {
|
|
|
|
static void *GDMIndex() {
|
|
|
|
static int RefBIndex = 0;
|
|
|
|
return &RefBIndex;
|
|
|
|
}
|
|
|
|
};
|
2009-11-13 09:54:21 +08:00
|
|
|
}
|
2010-12-23 02:53:20 +08:00
|
|
|
}
|
2009-11-13 09:54:21 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
2011-09-02 14:44:22 +08:00
|
|
|
// Function/Method behavior summaries.
|
2009-11-13 09:54:21 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class RetainSummary {
|
2012-03-18 09:26:10 +08:00
|
|
|
/// Args - a map of (index, ArgEffect) pairs, where index
|
2008-05-06 23:44:25 +08:00
|
|
|
/// specifies the argument (starting from 0). This can be sparsely
|
|
|
|
/// populated; arguments with no entry in Args use 'DefaultArgEffect'.
|
2009-05-03 13:20:50 +08:00
|
|
|
ArgEffects Args;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 23:44:25 +08:00
|
|
|
/// DefaultArgEffect - The default ArgEffect to apply to arguments that
|
|
|
|
/// do not have an entry in Args.
|
2012-01-04 08:35:45 +08:00
|
|
|
ArgEffect DefaultArgEffect;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// Receiver - If this summary applies to an Objective-C message expression,
|
|
|
|
/// this is the effect applied to the state of the receiver.
|
2012-01-04 08:35:45 +08:00
|
|
|
ArgEffect Receiver;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// Ret - The effect on the return value. Used to indicate if the
|
2011-08-22 05:58:18 +08:00
|
|
|
/// function/method call returns a new tracked symbol.
|
2012-01-04 08:35:45 +08:00
|
|
|
RetEffect Ret;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
public:
|
2009-05-03 13:20:50 +08:00
|
|
|
RetainSummary(ArgEffects A, RetEffect R, ArgEffect defaultEff,
|
2011-08-21 04:55:40 +08:00
|
|
|
ArgEffect ReceiverEff)
|
|
|
|
: Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// getArg - Return the argument effect on the argument specified by
|
|
|
|
/// idx (starting from 0).
|
2008-03-12 01:48:22 +08:00
|
|
|
ArgEffect getArg(unsigned idx) const {
|
2009-05-03 13:20:50 +08:00
|
|
|
if (const ArgEffect *AE = Args.lookup(idx))
|
|
|
|
return *AE;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 23:44:25 +08:00
|
|
|
return DefaultArgEffect;
|
2008-03-12 01:48:22 +08:00
|
|
|
}
|
2011-01-28 02:43:03 +08:00
|
|
|
|
|
|
|
void addArg(ArgEffects::Factory &af, unsigned idx, ArgEffect e) {
|
|
|
|
Args = af.add(Args, idx, e);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-04 13:31:22 +08:00
|
|
|
/// setDefaultArgEffect - Set the default argument effect.
|
|
|
|
void setDefaultArgEffect(ArgEffect E) {
|
|
|
|
DefaultArgEffect = E;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// getRetEffect - Returns the effect on the return value of the call.
|
2009-05-03 13:20:50 +08:00
|
|
|
RetEffect getRetEffect() const { return Ret; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-04 13:31:22 +08:00
|
|
|
/// setRetEffect - Set the effect of the return value of the call.
|
|
|
|
void setRetEffect(RetEffect E) { Ret = E; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-01-27 14:54:14 +08:00
|
|
|
|
|
|
|
/// Sets the effect on the receiver of the message.
|
|
|
|
void setReceiverEffect(ArgEffect e) { Receiver = e; }
|
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// getReceiverEffect - Returns the effect on the receiver of the call.
|
|
|
|
/// This is only meaningful if the summary applies to an ObjCMessageExpr*.
|
2009-05-03 13:20:50 +08:00
|
|
|
ArgEffect getReceiverEffect() const { return Receiver; }
|
2011-08-23 12:27:15 +08:00
|
|
|
|
|
|
|
/// Test if two retain summaries are identical. Note that merely equivalent
|
|
|
|
/// summaries are not necessarily identical (for example, if an explicit
|
|
|
|
/// argument effect matches the default effect).
|
|
|
|
bool operator==(const RetainSummary &Other) const {
|
|
|
|
return Args == Other.Args && DefaultArgEffect == Other.DefaultArgEffect &&
|
|
|
|
Receiver == Other.Receiver && Ret == Other.Ret;
|
|
|
|
}
|
2012-03-18 09:26:10 +08:00
|
|
|
|
|
|
|
/// Profile this summary for inclusion in a FoldingSet.
|
|
|
|
void Profile(llvm::FoldingSetNodeID& ID) const {
|
|
|
|
ID.Add(Args);
|
|
|
|
ID.Add(DefaultArgEffect);
|
|
|
|
ID.Add(Receiver);
|
|
|
|
ID.Add(Ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A retain summary is simple if it has no ArgEffects other than the default.
|
|
|
|
bool isSimple() const {
|
|
|
|
return Args.isEmpty();
|
|
|
|
}
|
2008-03-11 14:39:11 +08:00
|
|
|
};
|
2008-06-24 07:30:29 +08:00
|
|
|
} // end anonymous namespace
|
2008-03-11 14:39:11 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Data structures for constructing summaries.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-06-24 07:30:29 +08:00
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class ObjCSummaryKey {
|
2008-06-26 05:21:56 +08:00
|
|
|
IdentifierInfo* II;
|
|
|
|
Selector S;
|
2009-09-09 23:08:12 +08:00
|
|
|
public:
|
2008-06-26 05:21:56 +08:00
|
|
|
ObjCSummaryKey(IdentifierInfo* ii, Selector s)
|
|
|
|
: II(ii), S(s) {}
|
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
ObjCSummaryKey(const ObjCInterfaceDecl *d, Selector s)
|
2008-06-26 05:21:56 +08:00
|
|
|
: II(d ? d->getIdentifier() : 0), S(s) {}
|
2009-05-14 02:16:01 +08:00
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
ObjCSummaryKey(const ObjCInterfaceDecl *d, IdentifierInfo *ii, Selector s)
|
2009-05-14 02:16:01 +08:00
|
|
|
: II(d ? d->getIdentifier() : ii), S(s) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
ObjCSummaryKey(Selector s)
|
|
|
|
: II(0), S(s) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-01-04 08:35:45 +08:00
|
|
|
IdentifierInfo *getIdentifier() const { return II; }
|
2008-06-26 05:21:56 +08:00
|
|
|
Selector getSelector() const { return S; }
|
|
|
|
};
|
2008-06-24 07:30:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace llvm {
|
2008-06-26 05:21:56 +08:00
|
|
|
template <> struct DenseMapInfo<ObjCSummaryKey> {
|
|
|
|
static inline ObjCSummaryKey getEmptyKey() {
|
|
|
|
return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getEmptyKey(),
|
|
|
|
DenseMapInfo<Selector>::getEmptyKey());
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
static inline ObjCSummaryKey getTombstoneKey() {
|
|
|
|
return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getTombstoneKey(),
|
2009-09-09 23:08:12 +08:00
|
|
|
DenseMapInfo<Selector>::getTombstoneKey());
|
2008-06-26 05:21:56 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
static unsigned getHashValue(const ObjCSummaryKey &V) {
|
|
|
|
return (DenseMapInfo<IdentifierInfo*>::getHashValue(V.getIdentifier())
|
2009-09-09 23:08:12 +08:00
|
|
|
& 0x88888888)
|
2008-06-26 05:21:56 +08:00
|
|
|
| (DenseMapInfo<Selector>::getHashValue(V.getSelector())
|
|
|
|
& 0x55555555);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) {
|
|
|
|
return DenseMapInfo<IdentifierInfo*>::isEqual(LHS.getIdentifier(),
|
|
|
|
RHS.getIdentifier()) &&
|
|
|
|
DenseMapInfo<Selector>::isEqual(LHS.getSelector(),
|
|
|
|
RHS.getSelector());
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
};
|
2009-12-15 15:26:51 +08:00
|
|
|
template <>
|
|
|
|
struct isPodLike<ObjCSummaryKey> { static const bool value = true; };
|
2008-06-26 05:21:56 +08:00
|
|
|
} // end llvm namespace
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class ObjCSummaryCache {
|
2011-10-06 07:54:29 +08:00
|
|
|
typedef llvm::DenseMap<ObjCSummaryKey, const RetainSummary *> MapTy;
|
2008-06-26 05:21:56 +08:00
|
|
|
MapTy M;
|
|
|
|
public:
|
|
|
|
ObjCSummaryCache() {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary * find(const ObjCInterfaceDecl *D, IdentifierInfo *ClsName,
|
2009-04-30 07:03:22 +08:00
|
|
|
Selector S) {
|
2009-04-29 13:04:30 +08:00
|
|
|
// Lookup the method using the decl for the class @interface. If we
|
|
|
|
// have no decl, lookup using the class name.
|
|
|
|
return D ? find(D, S) : find(ClsName, S);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary * find(const ObjCInterfaceDecl *D, Selector S) {
|
2008-06-26 05:21:56 +08:00
|
|
|
// 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);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
if (I != M.end() || !D)
|
2009-07-22 07:27:57 +08:00
|
|
|
return I->second;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
// 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.
|
2011-08-13 07:37:29 +08:00
|
|
|
for (ObjCInterfaceDecl *C=D->getSuperClass() ;; C=C->getSuperClass()) {
|
2008-06-26 05:21:56 +08:00
|
|
|
if ((I = M.find(ObjCSummaryKey(C, S))) != M.end())
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
if (!C)
|
2009-07-22 07:27:57 +08:00
|
|
|
return NULL;
|
2008-06-24 07:30:29 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Cache the summary with original key to make the next lookup faster
|
2008-06-26 05:21:56 +08:00
|
|
|
// and return the iterator.
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *Summ = I->second;
|
2009-07-22 07:27:57 +08:00
|
|
|
M[K] = Summ;
|
|
|
|
return Summ;
|
2008-06-26 05:21:56 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-01-04 08:35:45 +08:00
|
|
|
const RetainSummary *find(IdentifierInfo* II, Selector S) {
|
2008-06-26 05:21:56 +08:00
|
|
|
// FIXME: Class method lookup. Right now we dont' have a good way
|
|
|
|
// of going between IdentifierInfo* and the class hierarchy.
|
2009-07-22 07:27:57 +08:00
|
|
|
MapTy::iterator I = M.find(ObjCSummaryKey(II, S));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
if (I == M.end())
|
|
|
|
I = M.find(ObjCSummaryKey(S));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
return I == M.end() ? NULL : I->second;
|
2008-06-26 05:21:56 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *& operator[](ObjCSummaryKey K) {
|
2008-06-26 05:21:56 +08:00
|
|
|
return M[K];
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *& operator[](Selector S) {
|
2008-06-26 05:21:56 +08:00
|
|
|
return M[ ObjCSummaryKey(S) ];
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
};
|
2008-06-26 05:21:56 +08:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Data structures for managing collections of summaries.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-06-24 07:30:29 +08:00
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class RetainSummaryManager {
|
2008-05-06 06:11:16 +08:00
|
|
|
|
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
// Typedefs.
|
|
|
|
//==-----------------------------------------------------------------==//
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
typedef llvm::DenseMap<const FunctionDecl*, const RetainSummary *>
|
2008-05-06 06:11:16 +08:00
|
|
|
FuncSummariesTy;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-24 07:30:29 +08:00
|
|
|
typedef ObjCSummaryCache ObjCMethodSummariesTy;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-18 09:26:10 +08:00
|
|
|
typedef llvm::FoldingSetNodeWrapper<RetainSummary> CachedSummaryNode;
|
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
// Data.
|
|
|
|
//==-----------------------------------------------------------------==//
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// Ctx - The ASTContext object for the analyzed ASTs.
|
2011-08-13 07:37:29 +08:00
|
|
|
ASTContext &Ctx;
|
2008-07-02 01:21:27 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// GCEnabled - Records whether or not the analyzed code runs in GC mode.
|
2008-04-29 13:33:51 +08:00
|
|
|
const bool GCEnabled;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-06-16 07:02:42 +08:00
|
|
|
/// Records whether or not the analyzed code runs in ARC mode.
|
|
|
|
const bool ARCEnabled;
|
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// FuncSummaries - A map from FunctionDecls to summaries.
|
2009-09-09 23:08:12 +08:00
|
|
|
FuncSummariesTy FuncSummaries;
|
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// ObjCClassMethodSummaries - A map from selectors (for instance methods)
|
|
|
|
/// to summaries.
|
2008-06-24 06:21:20 +08:00
|
|
|
ObjCMethodSummariesTy ObjCClassMethodSummaries;
|
2008-05-06 06:11:16 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// ObjCMethodSummaries - A map from selectors to summaries.
|
2008-06-24 06:21:20 +08:00
|
|
|
ObjCMethodSummariesTy ObjCMethodSummaries;
|
2008-05-06 06:11:16 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects,
|
|
|
|
/// and all other data used by the checker.
|
2008-05-06 06:11:16 +08:00
|
|
|
llvm::BumpPtrAllocator BPAlloc;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-03 13:20:50 +08:00
|
|
|
/// AF - A factory for ArgEffects objects.
|
2009-09-09 23:08:12 +08:00
|
|
|
ArgEffects::Factory AF;
|
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// ScratchArgs - A holding buffer for construct ArgEffects.
|
2012-01-04 08:35:45 +08:00
|
|
|
ArgEffects ScratchArgs;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-08 07:40:42 +08:00
|
|
|
/// ObjCAllocRetE - Default return effect for methods returning Objective-C
|
|
|
|
/// objects.
|
|
|
|
RetEffect ObjCAllocRetE;
|
2009-06-06 07:18:01 +08:00
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
/// ObjCInitRetE - Default return effect for init methods returning
|
2009-08-20 13:13:36 +08:00
|
|
|
/// Objective-C objects.
|
2009-06-06 07:18:01 +08:00
|
|
|
RetEffect ObjCInitRetE;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-18 09:26:10 +08:00
|
|
|
/// SimpleSummaries - Used for uniquing summaries that don't have special
|
|
|
|
/// effects.
|
|
|
|
llvm::FoldingSet<CachedSummaryNode> SimpleSummaries;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
//==-----------------------------------------------------------------==//
|
|
|
|
// Methods.
|
|
|
|
//==-----------------------------------------------------------------==//
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-06-26 05:21:56 +08:00
|
|
|
/// getArgEffects - Returns a persistent ArgEffects object based on the
|
|
|
|
/// data in ScratchArgs.
|
2009-05-03 13:20:50 +08:00
|
|
|
ArgEffects getArgEffects();
|
2008-03-12 09:21:45 +08:00
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable };
|
|
|
|
|
2008-10-23 09:56:15 +08:00
|
|
|
public:
|
2009-05-13 04:06:54 +08:00
|
|
|
RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
|
2011-10-06 07:54:29 +08:00
|
|
|
|
2012-01-04 08:35:45 +08:00
|
|
|
const RetainSummary *getUnarySummary(const FunctionType* FT,
|
2011-10-06 07:54:29 +08:00
|
|
|
UnaryFuncKind func);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-01-04 08:35:45 +08:00
|
|
|
const RetainSummary *getCFSummaryCreateRule(const FunctionDecl *FD);
|
|
|
|
const RetainSummary *getCFSummaryGetRule(const FunctionDecl *FD);
|
|
|
|
const RetainSummary *getCFCreateGetRuleSummary(const FunctionDecl *FD);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-18 09:26:10 +08:00
|
|
|
const RetainSummary *getPersistentSummary(const RetainSummary &OldSumm);
|
2008-10-29 12:07:07 +08:00
|
|
|
|
2012-03-18 09:26:10 +08:00
|
|
|
const RetainSummary *getPersistentSummary(RetEffect RetEff,
|
2011-10-06 07:54:29 +08:00
|
|
|
ArgEffect ReceiverEff = DoNothing,
|
|
|
|
ArgEffect DefaultEff = MayEscape) {
|
2012-03-18 09:26:10 +08:00
|
|
|
RetainSummary Summ(getArgEffects(), RetEff, DefaultEff, ReceiverEff);
|
|
|
|
return getPersistentSummary(Summ);
|
2008-05-06 08:30:21 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-18 09:26:10 +08:00
|
|
|
const RetainSummary *getDefaultSummary() {
|
|
|
|
return getPersistentSummary(RetEffect::MakeNoRet(),
|
|
|
|
DoNothing, MayEscape);
|
|
|
|
}
|
2008-10-29 12:07:07 +08:00
|
|
|
|
2012-03-18 09:26:10 +08:00
|
|
|
const RetainSummary *getPersistentStopSummary() {
|
|
|
|
return getPersistentSummary(RetEffect::MakeNoRet(),
|
|
|
|
StopTracking, StopTracking);
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2008-05-06 12:20:12 +08:00
|
|
|
|
2008-06-24 06:21:20 +08:00
|
|
|
void InitializeClassMethodSummaries();
|
|
|
|
void InitializeMethodSummaries();
|
2008-10-23 09:56:15 +08:00
|
|
|
private:
|
2011-10-06 07:54:29 +08:00
|
|
|
void addNSObjectClsMethSummary(Selector S, const RetainSummary *Summ) {
|
2008-06-26 05:21:56 +08:00
|
|
|
ObjCClassMethodSummaries[S] = Summ;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
void addNSObjectMethSummary(Selector S, const RetainSummary *Summ) {
|
2008-06-26 05:21:56 +08:00
|
|
|
ObjCMethodSummaries[S] = Summ;
|
|
|
|
}
|
2009-03-05 07:30:42 +08:00
|
|
|
|
2012-02-19 05:37:48 +08:00
|
|
|
void addClassMethSummary(const char* Cls, const char* name,
|
|
|
|
const RetainSummary *Summ, bool isNullary = true) {
|
2009-03-05 07:30:42 +08:00
|
|
|
IdentifierInfo* ClsII = &Ctx.Idents.get(Cls);
|
2012-02-19 05:37:48 +08:00
|
|
|
Selector S = isNullary ? GetNullarySelector(name, Ctx)
|
|
|
|
: GetUnarySelector(name, Ctx);
|
2009-03-05 07:30:42 +08:00
|
|
|
ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-25 10:54:57 +08:00
|
|
|
void addInstMethSummary(const char* Cls, const char* nullaryName,
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *Summ) {
|
2009-02-25 10:54:57 +08:00
|
|
|
IdentifierInfo* ClsII = &Ctx.Idents.get(Cls);
|
|
|
|
Selector S = GetNullarySelector(nullaryName, Ctx);
|
|
|
|
ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-25 01:50:11 +08:00
|
|
|
Selector generateSelector(va_list argp) {
|
2011-07-23 18:55:15 +08:00
|
|
|
SmallVector<IdentifierInfo*, 10> II;
|
2009-04-25 01:50:11 +08:00
|
|
|
|
2008-08-13 02:30:56 +08:00
|
|
|
while (const char* s = va_arg(argp, const char*))
|
|
|
|
II.push_back(&Ctx.Idents.get(s));
|
2009-04-25 01:50:11 +08:00
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
return Ctx.Selectors.getSelector(II.size(), &II[0]);
|
2009-04-25 01:50:11 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-25 01:50:11 +08:00
|
|
|
void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy& Summaries,
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary * Summ, va_list argp) {
|
2009-04-25 01:50:11 +08:00
|
|
|
Selector S = generateSelector(argp);
|
|
|
|
Summaries[ObjCSummaryKey(ClsII, S)] = Summ;
|
2008-07-19 01:24:20 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
void addInstMethSummary(const char* Cls, const RetainSummary * Summ, ...) {
|
2008-08-13 02:48:50 +08:00
|
|
|
va_list argp;
|
|
|
|
va_start(argp, Summ);
|
2009-04-25 01:50:11 +08:00
|
|
|
addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp);
|
2009-09-09 23:08:12 +08:00
|
|
|
va_end(argp);
|
2008-08-13 02:48:50 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
void addClsMethSummary(const char* Cls, const RetainSummary * Summ, ...) {
|
2009-04-25 01:50:11 +08:00
|
|
|
va_list argp;
|
|
|
|
va_start(argp, Summ);
|
|
|
|
addMethodSummary(&Ctx.Idents.get(Cls),ObjCClassMethodSummaries, Summ, argp);
|
|
|
|
va_end(argp);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
void addClsMethSummary(IdentifierInfo *II, const RetainSummary * Summ, ...) {
|
2009-04-25 01:50:11 +08:00
|
|
|
va_list argp;
|
|
|
|
va_start(argp, Summ);
|
|
|
|
addMethodSummary(II, ObjCClassMethodSummaries, Summ, argp);
|
|
|
|
va_end(argp);
|
|
|
|
}
|
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
public:
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
RetainSummaryManager(ASTContext &ctx, bool gcenabled, bool usesARC)
|
2008-07-02 01:21:27 +08:00
|
|
|
: Ctx(ctx),
|
2011-06-16 07:02:42 +08:00
|
|
|
GCEnabled(gcenabled),
|
|
|
|
ARCEnabled(usesARC),
|
|
|
|
AF(BPAlloc), ScratchArgs(AF.getEmptyMap()),
|
|
|
|
ObjCAllocRetE(gcenabled
|
|
|
|
? RetEffect::MakeGCNotOwned()
|
|
|
|
: (usesARC ? RetEffect::MakeARCNotOwned()
|
|
|
|
: RetEffect::MakeOwned(RetEffect::ObjC, true))),
|
|
|
|
ObjCInitRetE(gcenabled
|
|
|
|
? RetEffect::MakeGCNotOwned()
|
|
|
|
: (usesARC ? RetEffect::MakeARCNotOwned()
|
2012-03-18 09:26:10 +08:00
|
|
|
: RetEffect::MakeOwnedWhenTrackedReceiver())) {
|
2008-06-26 05:21:56 +08:00
|
|
|
InitializeClassMethodSummaries();
|
|
|
|
InitializeMethodSummaries();
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-01-04 08:35:45 +08:00
|
|
|
const RetainSummary *getSummary(const FunctionDecl *FD);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-18 05:13:07 +08:00
|
|
|
const RetainSummary *getMethodSummary(Selector S, IdentifierInfo *ClsName,
|
|
|
|
const ObjCInterfaceDecl *ID,
|
|
|
|
const ObjCMethodDecl *MD,
|
|
|
|
QualType RetTy,
|
|
|
|
ObjCMethodSummariesTy &CachedSummaries);
|
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg,
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state,
|
2011-10-06 07:54:29 +08:00
|
|
|
const LocationContext *LC);
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2012-01-04 08:35:45 +08:00
|
|
|
const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg,
|
2011-10-06 07:54:29 +08:00
|
|
|
const ObjCInterfaceDecl *ID) {
|
2012-03-18 05:13:07 +08:00
|
|
|
return getMethodSummary(msg.getSelector(), 0, ID, msg.getMethodDecl(),
|
|
|
|
msg.getType(Ctx), ObjCMethodSummaries);
|
2009-04-29 13:04:30 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *getClassMethodSummary(const ObjCMessage &msg) {
|
2011-01-25 08:03:53 +08:00
|
|
|
const ObjCInterfaceDecl *Class = 0;
|
|
|
|
if (!msg.isInstanceMessage())
|
|
|
|
Class = msg.getReceiverInterface();
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
|
2012-03-18 05:13:07 +08:00
|
|
|
return getMethodSummary(msg.getSelector(), Class->getIdentifier(),
|
|
|
|
Class, msg.getMethodDecl(), msg.getType(Ctx),
|
|
|
|
ObjCClassMethodSummaries);
|
2009-04-29 08:42:39 +08:00
|
|
|
}
|
2009-04-30 01:17:48 +08:00
|
|
|
|
|
|
|
/// getMethodSummary - This version of getMethodSummary is used to query
|
|
|
|
/// the summary for the current method being analyzed.
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) {
|
2009-04-30 07:03:22 +08:00
|
|
|
// FIXME: Eventually this should be unneeded.
|
|
|
|
const ObjCInterfaceDecl *ID = MD->getClassInterface();
|
2009-04-30 13:41:14 +08:00
|
|
|
Selector S = MD->getSelector();
|
2009-04-30 01:17:48 +08:00
|
|
|
IdentifierInfo *ClsName = ID->getIdentifier();
|
|
|
|
QualType ResultTy = MD->getResultType();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-18 05:13:07 +08:00
|
|
|
ObjCMethodSummariesTy *CachedSummaries;
|
2009-04-30 01:17:48 +08:00
|
|
|
if (MD->isInstanceMethod())
|
2012-03-18 05:13:07 +08:00
|
|
|
CachedSummaries = &ObjCMethodSummaries;
|
2009-04-30 01:17:48 +08:00
|
|
|
else
|
2012-03-18 05:13:07 +08:00
|
|
|
CachedSummaries = &ObjCClassMethodSummaries;
|
|
|
|
|
|
|
|
return getMethodSummary(S, ClsName, ID, MD, ResultTy, *CachedSummaries);
|
2009-04-30 01:17:48 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-18 05:13:07 +08:00
|
|
|
const RetainSummary *getStandardMethodSummary(const ObjCMethodDecl *MD,
|
2011-10-06 07:54:29 +08:00
|
|
|
Selector S, QualType RetTy);
|
2009-04-30 07:03:22 +08:00
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
void updateSummaryFromAnnotations(const RetainSummary *&Summ,
|
2009-05-09 10:58:13 +08:00
|
|
|
const ObjCMethodDecl *MD);
|
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
void updateSummaryFromAnnotations(const RetainSummary *&Summ,
|
2009-05-09 10:58:13 +08:00
|
|
|
const FunctionDecl *FD);
|
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
bool isGCEnabled() const { return GCEnabled; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-06-16 07:02:42 +08:00
|
|
|
bool isARCEnabled() const { return ARCEnabled; }
|
|
|
|
|
|
|
|
bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; }
|
2008-03-11 14:39:11 +08:00
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-24 17:02:37 +08:00
|
|
|
// Used to avoid allocating long-term (BPAlloc'd) memory for default retain
|
|
|
|
// summaries. If a function or method looks like it has a default summary, but
|
|
|
|
// it has annotations, the annotations are added to the stack-based template
|
|
|
|
// and then copied into managed memory.
|
|
|
|
class RetainSummaryTemplate {
|
|
|
|
RetainSummaryManager &Manager;
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *&RealSummary;
|
2011-08-24 17:02:37 +08:00
|
|
|
RetainSummary ScratchSummary;
|
|
|
|
bool Accessed;
|
|
|
|
public:
|
2012-03-18 03:53:04 +08:00
|
|
|
RetainSummaryTemplate(const RetainSummary *&real, const RetainSummary &base,
|
|
|
|
RetainSummaryManager &mgr)
|
|
|
|
: Manager(mgr), RealSummary(real), ScratchSummary(real ? *real : base),
|
2011-10-06 07:54:29 +08:00
|
|
|
Accessed(false) {}
|
2011-08-24 17:02:37 +08:00
|
|
|
|
|
|
|
~RetainSummaryTemplate() {
|
2011-10-06 07:54:29 +08:00
|
|
|
if (Accessed)
|
2012-03-18 09:26:10 +08:00
|
|
|
RealSummary = Manager.getPersistentSummary(ScratchSummary);
|
2011-08-24 17:02:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
RetainSummary &operator*() {
|
|
|
|
Accessed = true;
|
2011-10-06 07:54:29 +08:00
|
|
|
return ScratchSummary;
|
2011-08-24 17:02:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
RetainSummary *operator->() {
|
|
|
|
Accessed = true;
|
2011-10-06 07:54:29 +08:00
|
|
|
return &ScratchSummary;
|
2011-08-24 17:02:37 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Implementation of checker data structures.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2009-05-03 13:20:50 +08:00
|
|
|
ArgEffects RetainSummaryManager::getArgEffects() {
|
|
|
|
ArgEffects AE = ScratchArgs;
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.getEmptyMap();
|
2009-05-03 13:20:50 +08:00
|
|
|
return AE;
|
2008-03-12 09:21:45 +08:00
|
|
|
}
|
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *
|
2012-03-18 09:26:10 +08:00
|
|
|
RetainSummaryManager::getPersistentSummary(const RetainSummary &OldSumm) {
|
|
|
|
// Unique "simple" summaries -- those without ArgEffects.
|
|
|
|
if (OldSumm.isSimple()) {
|
|
|
|
llvm::FoldingSetNodeID ID;
|
|
|
|
OldSumm.Profile(ID);
|
|
|
|
|
|
|
|
void *Pos;
|
|
|
|
CachedSummaryNode *N = SimpleSummaries.FindNodeOrInsertPos(ID, Pos);
|
|
|
|
|
|
|
|
if (!N) {
|
|
|
|
N = (CachedSummaryNode *) BPAlloc.Allocate<CachedSummaryNode>();
|
|
|
|
new (N) CachedSummaryNode(OldSumm);
|
|
|
|
SimpleSummaries.InsertNode(N, Pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
return &N->getValue();
|
|
|
|
}
|
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>();
|
2012-03-18 09:26:10 +08:00
|
|
|
new (Summ) RetainSummary(OldSumm);
|
2008-03-12 09:21:45 +08:00
|
|
|
return Summ;
|
|
|
|
}
|
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Summary creation for functions (largely uses of Core Foundation).
|
|
|
|
//===----------------------------------------------------------------------===//
|
2008-03-12 09:21:45 +08:00
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
static bool isRetain(const FunctionDecl *FD, StringRef FName) {
|
2010-02-09 02:38:55 +08:00
|
|
|
return FName.endswith("Retain");
|
2009-01-13 05:45:02 +08:00
|
|
|
}
|
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
static bool isRelease(const FunctionDecl *FD, StringRef FName) {
|
2010-02-09 02:38:55 +08:00
|
|
|
return FName.endswith("Release");
|
2009-01-13 05:45:02 +08:00
|
|
|
}
|
|
|
|
|
2011-08-22 05:58:18 +08:00
|
|
|
static bool isMakeCollectable(const FunctionDecl *FD, StringRef FName) {
|
|
|
|
// FIXME: Remove FunctionDecl parameter.
|
|
|
|
// FIXME: Is it really okay if MakeCollectable isn't a suffix?
|
|
|
|
return FName.find("MakeCollectable") != StringRef::npos;
|
|
|
|
}
|
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
|
2008-04-25 01:22:33 +08:00
|
|
|
// Look up a summary in our cache of FunctionDecls -> Summaries.
|
2008-05-06 06:11:16 +08:00
|
|
|
FuncSummariesTy::iterator I = FuncSummaries.find(FD);
|
|
|
|
if (I != FuncSummaries.end())
|
2008-04-25 01:22:33 +08:00
|
|
|
return I->second;
|
|
|
|
|
2009-05-04 23:34:07 +08:00
|
|
|
// No summary? Generate one.
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *S = 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-07-16 00:50:12 +08:00
|
|
|
do {
|
2009-01-13 05:45:02 +08:00
|
|
|
// We generate "stop" summaries for implicitly defined functions.
|
|
|
|
if (FD->isImplicit()) {
|
|
|
|
S = getPersistentStopSummary();
|
|
|
|
break;
|
|
|
|
}
|
2011-05-03 05:21:42 +08:00
|
|
|
// For C++ methods, generate an implicit "stop" summary as well. We
|
|
|
|
// can relax this once we have a clear policy for C++ methods and
|
|
|
|
// ownership attributes.
|
|
|
|
if (isa<CXXMethodDecl>(FD)) {
|
|
|
|
S = getPersistentStopSummary();
|
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-09-22 07:43:11 +08:00
|
|
|
// [PR 3337] Use 'getAs<FunctionType>' to strip away any typedefs on the
|
2009-01-17 02:40:33 +08:00
|
|
|
// function's type.
|
2009-09-22 07:43:11 +08:00
|
|
|
const FunctionType* FT = FD->getType()->getAs<FunctionType>();
|
2009-12-16 14:06:43 +08:00
|
|
|
const IdentifierInfo *II = FD->getIdentifier();
|
|
|
|
if (!II)
|
|
|
|
break;
|
2010-02-09 02:38:55 +08:00
|
|
|
|
|
|
|
StringRef FName = II->getName();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-06 06:11:14 +08:00
|
|
|
// Strip away preceding '_'. Doing this here will effect all the checks
|
|
|
|
// down below.
|
2010-02-09 02:38:55 +08:00
|
|
|
FName = FName.substr(FName.find_first_not_of('_'));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-01-13 05:45:02 +08:00
|
|
|
// Inspect the result type.
|
|
|
|
QualType RetTy = FT->getResultType();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-01-13 05:45:02 +08:00
|
|
|
// FIXME: This should all be refactored into a chain of "summary lookup"
|
|
|
|
// filters.
|
2009-10-14 08:27:24 +08:00
|
|
|
assert(ScratchArgs.isEmpty());
|
2010-02-09 00:45:01 +08:00
|
|
|
|
2010-02-09 02:38:55 +08:00
|
|
|
if (FName == "pthread_create") {
|
|
|
|
// Part of: <rdar://problem/7299394>. This will be addressed
|
|
|
|
// better with IPA.
|
|
|
|
S = getPersistentStopSummary();
|
|
|
|
} else if (FName == "NSMakeCollectable") {
|
|
|
|
// Handle: id NSMakeCollectable(CFTypeRef)
|
|
|
|
S = (RetTy->isObjCIdType())
|
|
|
|
? getUnarySummary(FT, cfmakecollectable)
|
|
|
|
: getPersistentStopSummary();
|
|
|
|
} else if (FName == "IOBSDNameMatching" ||
|
|
|
|
FName == "IOServiceMatching" ||
|
|
|
|
FName == "IOServiceNameMatching" ||
|
|
|
|
FName == "IORegistryEntryIDMatching" ||
|
|
|
|
FName == "IOOpenFirmwarePathMatching") {
|
|
|
|
// Part of <rdar://problem/6961230>. (IOKit)
|
|
|
|
// This should be addressed using a API table.
|
|
|
|
S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true),
|
|
|
|
DoNothing, DoNothing);
|
|
|
|
} else if (FName == "IOServiceGetMatchingService" ||
|
|
|
|
FName == "IOServiceGetMatchingServices") {
|
|
|
|
// 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.
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.add(ScratchArgs, 1, DecRef);
|
2010-02-09 02:38:55 +08:00
|
|
|
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
|
|
|
} else if (FName == "IOServiceAddNotification" ||
|
|
|
|
FName == "IOServiceAddMatchingNotification") {
|
|
|
|
// Part of <rdar://problem/6961230>. (IOKit)
|
|
|
|
// This should be addressed using a API table.
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.add(ScratchArgs, 2, DecRef);
|
2010-02-09 02:38:55 +08:00
|
|
|
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
|
|
|
} else if (FName == "CVPixelBufferCreateWithBytes") {
|
|
|
|
// FIXES: <rdar://problem/7283567>
|
|
|
|
// Eventually this can be improved by recognizing that the pixel
|
|
|
|
// buffer passed to CVPixelBufferCreateWithBytes is released via
|
|
|
|
// a callback and doing full IPA to make sure this is done correctly.
|
|
|
|
// FIXME: This function has an out parameter that returns an
|
|
|
|
// allocated object.
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.add(ScratchArgs, 7, StopTracking);
|
2010-02-09 02:38:55 +08:00
|
|
|
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
|
|
|
} else if (FName == "CGBitmapContextCreateWithData") {
|
|
|
|
// FIXES: <rdar://problem/7358899>
|
|
|
|
// Eventually this can be improved by recognizing that 'releaseInfo'
|
|
|
|
// passed to CGBitmapContextCreateWithData is released via
|
|
|
|
// a callback and doing full IPA to make sure this is done correctly.
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.add(ScratchArgs, 8, StopTracking);
|
2010-02-09 02:38:55 +08:00
|
|
|
S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true),
|
|
|
|
DoNothing, DoNothing);
|
|
|
|
} else if (FName == "CVPixelBufferCreateWithPlanarBytes") {
|
|
|
|
// FIXES: <rdar://problem/7283567>
|
|
|
|
// Eventually this can be improved by recognizing that the pixel
|
|
|
|
// buffer passed to CVPixelBufferCreateWithPlanarBytes is released
|
|
|
|
// via a callback and doing full IPA to make sure this is done
|
|
|
|
// correctly.
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.add(ScratchArgs, 12, StopTracking);
|
2010-02-09 02:38:55 +08:00
|
|
|
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
2012-03-22 14:29:41 +08:00
|
|
|
} else if (FName == "dispatch_set_context") {
|
|
|
|
// <rdar://problem/11059275> - The analyzer currently doesn't have
|
|
|
|
// a good way to reason about the finalizer function for libdispatch.
|
|
|
|
// If we pass a context object that is memory managed, stop tracking it.
|
|
|
|
// FIXME: this hack should possibly go away once we can handle
|
|
|
|
// libdispatch finalizers.
|
|
|
|
ScratchArgs = AF.add(ScratchArgs, 1, StopTracking);
|
|
|
|
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
2009-06-12 02:17:24 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-12 02:17:24 +08:00
|
|
|
// Did we get a summary?
|
|
|
|
if (S)
|
|
|
|
break;
|
2009-03-18 06:43:44 +08:00
|
|
|
|
|
|
|
// Enable this code once the semantics of NSDeallocateObject are resolved
|
|
|
|
// for GC. <rdar://problem/6619988>
|
|
|
|
#if 0
|
|
|
|
// Handle: NSDeallocateObject(id anObject);
|
|
|
|
// This method does allow 'nil' (although we don't check it now).
|
2009-09-09 23:08:12 +08:00
|
|
|
if (strcmp(FName, "NSDeallocateObject") == 0) {
|
2009-03-18 06:43:44 +08:00
|
|
|
return RetTy == Ctx.VoidTy
|
|
|
|
? getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, Dealloc)
|
|
|
|
: getPersistentStopSummary();
|
|
|
|
}
|
|
|
|
#endif
|
2009-01-13 05:45:02 +08:00
|
|
|
|
|
|
|
if (RetTy->isPointerType()) {
|
|
|
|
// For CoreFoundation ('CF') types.
|
2010-01-28 02:00:17 +08:00
|
|
|
if (cocoa::isRefType(RetTy, "CF", FName)) {
|
2009-01-13 05:45:02 +08:00
|
|
|
if (isRetain(FD, FName))
|
|
|
|
S = getUnarySummary(FT, cfretain);
|
2011-08-22 05:58:18 +08:00
|
|
|
else if (isMakeCollectable(FD, FName))
|
2009-01-13 05:45:02 +08:00
|
|
|
S = getUnarySummary(FT, cfmakecollectable);
|
2009-09-09 23:08:12 +08:00
|
|
|
else
|
2011-10-01 08:48:56 +08:00
|
|
|
S = getCFCreateGetRuleSummary(FD);
|
2009-01-13 05:45:02 +08:00
|
|
|
|
2008-07-16 00:50:12 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-01-13 05:45:02 +08:00
|
|
|
|
|
|
|
// For CoreGraphics ('CG') types.
|
2010-01-28 02:00:17 +08:00
|
|
|
if (cocoa::isRefType(RetTy, "CG", FName)) {
|
2009-01-13 05:45:02 +08:00
|
|
|
if (isRetain(FD, FName))
|
|
|
|
S = getUnarySummary(FT, cfretain);
|
|
|
|
else
|
2011-10-01 08:48:56 +08:00
|
|
|
S = getCFCreateGetRuleSummary(FD);
|
2009-01-13 05:45:02 +08:00
|
|
|
|
2008-07-16 00:50:12 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-01-13 05:45:02 +08:00
|
|
|
|
|
|
|
// For the Disk Arbitration API (DiskArbitration/DADisk.h)
|
2010-01-28 02:00:17 +08:00
|
|
|
if (cocoa::isRefType(RetTy, "DADisk") ||
|
|
|
|
cocoa::isRefType(RetTy, "DADissenter") ||
|
|
|
|
cocoa::isRefType(RetTy, "DASessionRef")) {
|
2011-10-01 08:48:56 +08:00
|
|
|
S = getCFCreateGetRuleSummary(FD);
|
2008-10-29 12:07:07 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-01-13 05:45:02 +08:00
|
|
|
break;
|
2008-07-16 00:50:12 +08:00
|
|
|
}
|
2009-01-13 05:45:02 +08: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')) {
|
2010-02-09 00:45:01 +08:00
|
|
|
// Test for 'CGCF'.
|
2010-02-09 02:38:55 +08:00
|
|
|
FName = FName.substr(FName.startswith("CGCF") ? 4 : 2);
|
2010-02-09 00:45:01 +08:00
|
|
|
|
2009-03-06 06:11:14 +08:00
|
|
|
if (isRelease(FD, FName))
|
2009-01-13 05:45:02 +08:00
|
|
|
S = getUnarySummary(FT, cfrelease);
|
|
|
|
else {
|
2009-05-03 13:20:50 +08:00
|
|
|
assert (ScratchArgs.isEmpty());
|
2009-01-30 06:45:13 +08:00
|
|
|
// 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:
|
|
|
|
//
|
2009-09-09 23:08:12 +08:00
|
|
|
// CFMutableDictionaryRef x = CFDictionaryCreateMutable(...);
|
2009-01-30 06:45:13 +08:00
|
|
|
// CFDictionaryAddValue(y, key, x);
|
2009-09-09 23:08:12 +08:00
|
|
|
// CFRelease(x);
|
2009-01-30 06:45:13 +08:00
|
|
|
// ... 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
|
2009-08-20 08:57:22 +08:00
|
|
|
// function name contains "InsertValue", "SetValue", "AddValue",
|
|
|
|
// "AppendValue", or "SetAttribute", then we assume that arguments may
|
|
|
|
// "escape." This means that something else holds on to the object,
|
|
|
|
// allowing it be used even after its local retain count drops to 0.
|
2010-01-12 03:46:28 +08:00
|
|
|
ArgEffect E = (StrInStrNoCase(FName, "InsertValue") != StringRef::npos||
|
|
|
|
StrInStrNoCase(FName, "AddValue") != StringRef::npos ||
|
|
|
|
StrInStrNoCase(FName, "SetValue") != StringRef::npos ||
|
|
|
|
StrInStrNoCase(FName, "AppendValue") != StringRef::npos||
|
2010-01-12 04:15:06 +08:00
|
|
|
StrInStrNoCase(FName, "SetAttribute") != StringRef::npos)
|
2009-01-30 06:45:13 +08:00
|
|
|
? MayEscape : DoNothing;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-01-30 06:45:13 +08:00
|
|
|
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, E);
|
2009-01-13 05:45:02 +08:00
|
|
|
}
|
2008-10-23 04:54:52 +08:00
|
|
|
}
|
2008-07-16 00:50:12 +08:00
|
|
|
}
|
|
|
|
while (0);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 10:58:13 +08:00
|
|
|
// Annotations override defaults.
|
2011-08-23 12:27:15 +08:00
|
|
|
updateSummaryFromAnnotations(S, FD);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
FuncSummaries[FD] = S;
|
2009-09-09 23:08:12 +08:00
|
|
|
return S;
|
2008-03-06 08:08:09 +08:00
|
|
|
}
|
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *
|
2011-10-01 08:48:56 +08:00
|
|
|
RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) {
|
|
|
|
if (coreFoundation::followsCreateRule(FD))
|
2008-05-06 00:51:50 +08:00
|
|
|
return getCFSummaryCreateRule(FD);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-05-25 14:19:45 +08:00
|
|
|
return getCFSummaryGetRule(FD);
|
2008-03-12 09:21:45 +08:00
|
|
|
}
|
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *
|
2009-02-24 00:51:39 +08:00
|
|
|
RetainSummaryManager::getUnarySummary(const FunctionType* FT,
|
|
|
|
UnaryFuncKind func) {
|
|
|
|
|
2009-01-13 05:45:02 +08:00
|
|
|
// Sanity check that this is *really* a unary function. This can
|
|
|
|
// happen if people do weird things.
|
2009-02-27 07:50:07 +08:00
|
|
|
const FunctionProtoType* FTP = dyn_cast<FunctionProtoType>(FT);
|
2009-01-13 05:45:02 +08:00
|
|
|
if (!FTP || FTP->getNumArgs() != 1)
|
|
|
|
return getPersistentStopSummary();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-03 13:20:50 +08:00
|
|
|
assert (ScratchArgs.isEmpty());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-22 05:58:18 +08:00
|
|
|
ArgEffect Effect;
|
2008-04-29 13:33:51 +08:00
|
|
|
switch (func) {
|
2011-08-22 05:58:18 +08:00
|
|
|
case cfretain: Effect = IncRef; break;
|
|
|
|
case cfrelease: Effect = DecRef; break;
|
|
|
|
case cfmakecollectable: Effect = MakeCollectable; break;
|
2008-04-11 07:44:06 +08:00
|
|
|
}
|
2011-08-22 05:58:18 +08:00
|
|
|
|
|
|
|
ScratchArgs = AF.add(ScratchArgs, 0, Effect);
|
|
|
|
return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
2008-03-12 09:21:45 +08:00
|
|
|
}
|
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *
|
2011-08-13 07:37:29 +08:00
|
|
|
RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) {
|
2009-05-03 13:20:50 +08:00
|
|
|
assert (ScratchArgs.isEmpty());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-01-28 13:56:51 +08:00
|
|
|
return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true));
|
2008-03-12 09:21:45 +08:00
|
|
|
}
|
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *
|
2011-08-13 07:37:29 +08:00
|
|
|
RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) {
|
2009-09-09 23:08:12 +08:00
|
|
|
assert (ScratchArgs.isEmpty());
|
2009-01-28 13:56:51 +08:00
|
|
|
return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF),
|
|
|
|
DoNothing, DoNothing);
|
2008-03-12 09:21:45 +08:00
|
|
|
}
|
|
|
|
|
2008-05-06 06:11:16 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Summary creation for Selectors.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2009-05-09 10:58:13 +08:00
|
|
|
void
|
2011-10-06 07:54:29 +08:00
|
|
|
RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
|
2009-05-09 10:58:13 +08:00
|
|
|
const FunctionDecl *FD) {
|
|
|
|
if (!FD)
|
|
|
|
return;
|
|
|
|
|
2012-03-18 09:26:10 +08:00
|
|
|
RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this);
|
2011-08-23 12:27:15 +08:00
|
|
|
|
2011-01-28 02:43:03 +08:00
|
|
|
// Effects on the parameters.
|
|
|
|
unsigned parm_idx = 0;
|
|
|
|
for (FunctionDecl::param_const_iterator pi = FD->param_begin(),
|
2011-04-06 17:02:12 +08:00
|
|
|
pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) {
|
2011-01-28 02:43:03 +08:00
|
|
|
const ParmVarDecl *pd = *pi;
|
|
|
|
if (pd->getAttr<NSConsumedAttr>()) {
|
2011-08-23 12:27:15 +08:00
|
|
|
if (!GCEnabled) {
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->addArg(AF, parm_idx, DecRef);
|
2011-08-23 12:27:15 +08:00
|
|
|
}
|
|
|
|
} else if (pd->getAttr<CFConsumedAttr>()) {
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->addArg(AF, parm_idx, DecRef);
|
2011-01-28 02:43:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-12 02:17:24 +08:00
|
|
|
QualType RetTy = FD->getResultType();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 10:58:13 +08:00
|
|
|
// Determine if there is a special return effect for this method.
|
2010-01-28 02:00:17 +08:00
|
|
|
if (cocoa::isCocoaObjectRef(RetTy)) {
|
2009-06-30 10:34:44 +08:00
|
|
|
if (FD->getAttr<NSReturnsRetainedAttr>()) {
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->setRetEffect(ObjCAllocRetE);
|
2009-05-09 10:58:13 +08:00
|
|
|
}
|
2009-06-30 10:34:44 +08:00
|
|
|
else if (FD->getAttr<CFReturnsRetainedAttr>()) {
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
|
2009-06-12 02:17:24 +08:00
|
|
|
}
|
2010-02-18 08:06:12 +08:00
|
|
|
else if (FD->getAttr<NSReturnsNotRetainedAttr>()) {
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC));
|
2010-02-18 08:06:12 +08:00
|
|
|
}
|
|
|
|
else if (FD->getAttr<CFReturnsNotRetainedAttr>()) {
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
|
2010-02-18 08:06:12 +08:00
|
|
|
}
|
2011-08-23 12:27:15 +08:00
|
|
|
} else if (RetTy->getAs<PointerType>()) {
|
2009-06-30 10:34:44 +08:00
|
|
|
if (FD->getAttr<CFReturnsRetainedAttr>()) {
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
|
2009-05-09 10:58:13 +08:00
|
|
|
}
|
2011-05-25 14:29:39 +08:00
|
|
|
else if (FD->getAttr<CFReturnsNotRetainedAttr>()) {
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
|
2011-05-25 14:29:39 +08:00
|
|
|
}
|
2009-05-09 10:58:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-10-06 07:54:29 +08:00
|
|
|
RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
|
|
|
|
const ObjCMethodDecl *MD) {
|
2009-05-09 10:58:13 +08:00
|
|
|
if (!MD)
|
|
|
|
return;
|
|
|
|
|
2012-03-18 09:26:10 +08:00
|
|
|
RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this);
|
2009-07-07 02:30:43 +08:00
|
|
|
bool isTrackedLoc = false;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-01-27 14:54:14 +08:00
|
|
|
// Effects on the receiver.
|
|
|
|
if (MD->getAttr<NSConsumesSelfAttr>()) {
|
2011-01-28 02:43:03 +08:00
|
|
|
if (!GCEnabled)
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->setReceiverEffect(DecRefMsg);
|
2011-01-28 02:43:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Effects on the parameters.
|
|
|
|
unsigned parm_idx = 0;
|
2011-10-03 14:37:04 +08:00
|
|
|
for (ObjCMethodDecl::param_const_iterator
|
|
|
|
pi=MD->param_begin(), pe=MD->param_end();
|
2011-01-28 02:43:03 +08:00
|
|
|
pi != pe; ++pi, ++parm_idx) {
|
|
|
|
const ParmVarDecl *pd = *pi;
|
|
|
|
if (pd->getAttr<NSConsumedAttr>()) {
|
|
|
|
if (!GCEnabled)
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->addArg(AF, parm_idx, DecRef);
|
2011-01-28 02:43:03 +08:00
|
|
|
}
|
|
|
|
else if(pd->getAttr<CFConsumedAttr>()) {
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->addArg(AF, parm_idx, DecRef);
|
2011-01-28 02:43:03 +08:00
|
|
|
}
|
2011-01-27 14:54:14 +08:00
|
|
|
}
|
|
|
|
|
2009-05-09 10:58:13 +08:00
|
|
|
// Determine if there is a special return effect for this method.
|
2010-01-28 02:00:17 +08:00
|
|
|
if (cocoa::isCocoaObjectRef(MD->getResultType())) {
|
2009-06-30 10:34:44 +08:00
|
|
|
if (MD->getAttr<NSReturnsRetainedAttr>()) {
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->setRetEffect(ObjCAllocRetE);
|
2009-07-07 02:30:43 +08:00
|
|
|
return;
|
2009-05-09 10:58:13 +08:00
|
|
|
}
|
2010-02-18 08:06:12 +08:00
|
|
|
if (MD->getAttr<NSReturnsNotRetainedAttr>()) {
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC));
|
2010-02-18 08:06:12 +08:00
|
|
|
return;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-07 02:30:43 +08:00
|
|
|
isTrackedLoc = true;
|
2011-08-24 17:02:37 +08:00
|
|
|
} else {
|
2009-07-30 05:53:49 +08:00
|
|
|
isTrackedLoc = MD->getResultType()->getAs<PointerType>() != NULL;
|
2011-08-24 17:02:37 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2010-02-18 08:06:12 +08:00
|
|
|
if (isTrackedLoc) {
|
|
|
|
if (MD->getAttr<CFReturnsRetainedAttr>())
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
|
2010-02-18 08:06:12 +08:00
|
|
|
else if (MD->getAttr<CFReturnsNotRetainedAttr>())
|
2011-08-24 17:02:37 +08:00
|
|
|
Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
|
2010-02-18 08:06:12 +08:00
|
|
|
}
|
2009-05-09 10:58:13 +08:00
|
|
|
}
|
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *
|
2012-03-18 05:13:07 +08:00
|
|
|
RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD,
|
|
|
|
Selector S, QualType RetTy) {
|
2009-04-25 05:56:17 +08:00
|
|
|
|
2009-04-29 08:42:39 +08:00
|
|
|
if (MD) {
|
2009-04-25 02:00:17 +08:00
|
|
|
// Scan the method decl for 'void*' arguments. These should be treated
|
|
|
|
// as 'StopTracking' because they are often used with delegates.
|
|
|
|
// Delegates are a frequent form of false positives with the retain
|
|
|
|
// count checker.
|
|
|
|
unsigned i = 0;
|
2011-10-03 14:37:04 +08:00
|
|
|
for (ObjCMethodDecl::param_const_iterator I = MD->param_begin(),
|
2009-04-25 02:00:17 +08:00
|
|
|
E = MD->param_end(); I != E; ++I, ++i)
|
2011-10-03 14:37:04 +08:00
|
|
|
if (const ParmVarDecl *PD = *I) {
|
2009-04-25 02:00:17 +08:00
|
|
|
QualType Ty = Ctx.getCanonicalType(PD->getType());
|
First part of changes to eliminate problems with cv-qualifiers and
sugared types. The basic problem is that our qualifier accessors
(getQualifiers, getCVRQualifiers, isConstQualified, etc.) only look at
the current QualType and not at any qualifiers that come from sugared
types, meaning that we won't see these qualifiers through, e.g.,
typedefs:
typedef const int CInt;
typedef CInt Self;
Self.isConstQualified() currently returns false!
Various bugs (e.g., PR5383) have cropped up all over the front end due
to such problems. I'm addressing this problem by splitting each
qualifier accessor into two versions:
- the "local" version only returns qualifiers on this particular
QualType instance
- the "normal" version that will eventually combine qualifiers from this
QualType instance with the qualifiers on the canonical type to
produce the full set of qualifiers.
This commit adds the local versions and switches a few callers from
the "normal" version (e.g., isConstQualified) over to the "local"
version (e.g., isLocalConstQualified) when that is the right thing to
do, e.g., because we're printing or serializing the qualifiers. Also,
switch a bunch of
Context.getCanonicalType(T1).getUnqualifiedType() == Context.getCanonicalType(T2).getQualifiedType()
expressions over to
Context.hasSameUnqualifiedType(T1, T2)
llvm-svn: 88969
2009-11-17 05:35:15 +08:00
|
|
|
if (Ty.getLocalUnqualifiedType() == Ctx.VoidPtrTy)
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.add(ScratchArgs, i, StopTracking);
|
2009-04-25 02:00:17 +08:00
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-18 03:53:04 +08:00
|
|
|
// Any special effects?
|
2009-04-25 05:56:17 +08:00
|
|
|
ArgEffect ReceiverEff = DoNothing;
|
2012-03-18 03:53:04 +08:00
|
|
|
RetEffect ResultEff = RetEffect::MakeNoRet();
|
|
|
|
|
|
|
|
// Check the method family, and apply any default annotations.
|
|
|
|
switch (MD ? MD->getMethodFamily() : S.getMethodFamily()) {
|
|
|
|
case OMF_None:
|
|
|
|
case OMF_performSelector:
|
|
|
|
// Assume all Objective-C methods follow Cocoa Memory Management rules.
|
|
|
|
// FIXME: Does the non-threaded performSelector family really belong here?
|
|
|
|
// The selector could be, say, @selector(copy).
|
|
|
|
if (cocoa::isCocoaObjectRef(RetTy))
|
|
|
|
ResultEff = RetEffect::MakeNotOwned(RetEffect::ObjC);
|
|
|
|
else if (coreFoundation::isCFObjectRef(RetTy)) {
|
|
|
|
// ObjCMethodDecl currently doesn't consider CF objects as valid return
|
|
|
|
// values for alloc, new, copy, or mutableCopy, so we have to
|
|
|
|
// double-check with the selector. This is ugly, but there aren't that
|
|
|
|
// many Objective-C methods that return CF objects, right?
|
|
|
|
if (MD) {
|
|
|
|
switch (S.getMethodFamily()) {
|
|
|
|
case OMF_alloc:
|
|
|
|
case OMF_new:
|
|
|
|
case OMF_copy:
|
|
|
|
case OMF_mutableCopy:
|
|
|
|
ResultEff = RetEffect::MakeOwned(RetEffect::CF, true);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ResultEff = RetEffect::MakeNotOwned(RetEffect::CF);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ResultEff = RetEffect::MakeNotOwned(RetEffect::CF);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OMF_init:
|
|
|
|
ResultEff = ObjCInitRetE;
|
|
|
|
ReceiverEff = DecRefMsg;
|
|
|
|
break;
|
|
|
|
case OMF_alloc:
|
|
|
|
case OMF_new:
|
|
|
|
case OMF_copy:
|
|
|
|
case OMF_mutableCopy:
|
|
|
|
if (cocoa::isCocoaObjectRef(RetTy))
|
|
|
|
ResultEff = ObjCAllocRetE;
|
|
|
|
else if (coreFoundation::isCFObjectRef(RetTy))
|
|
|
|
ResultEff = RetEffect::MakeOwned(RetEffect::CF, true);
|
|
|
|
break;
|
|
|
|
case OMF_autorelease:
|
|
|
|
ReceiverEff = Autorelease;
|
|
|
|
break;
|
|
|
|
case OMF_retain:
|
|
|
|
ReceiverEff = IncRefMsg;
|
|
|
|
break;
|
|
|
|
case OMF_release:
|
|
|
|
ReceiverEff = DecRefMsg;
|
|
|
|
break;
|
|
|
|
case OMF_dealloc:
|
|
|
|
ReceiverEff = Dealloc;
|
|
|
|
break;
|
|
|
|
case OMF_self:
|
|
|
|
// -self is handled specially by the ExprEngine to propagate the receiver.
|
|
|
|
break;
|
|
|
|
case OMF_retainCount:
|
|
|
|
case OMF_finalize:
|
|
|
|
// These methods don't return objects.
|
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-25 05:56:17 +08:00
|
|
|
// If one of the arguments in the selector has the keyword 'delegate' we
|
|
|
|
// should stop tracking the reference count for the receiver. This is
|
|
|
|
// because the reference count is quite possibly handled by a delegate
|
|
|
|
// method.
|
|
|
|
if (S.isKeywordSelector()) {
|
|
|
|
const std::string &str = S.getAsString();
|
|
|
|
assert(!str.empty());
|
2010-01-12 03:46:28 +08:00
|
|
|
if (StrInStrNoCase(str, "delegate:") != StringRef::npos)
|
|
|
|
ReceiverEff = StopTracking;
|
2009-04-25 05:56:17 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-18 03:53:04 +08:00
|
|
|
if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing &&
|
|
|
|
ResultEff.getKind() == RetEffect::NoRet)
|
2011-10-06 07:54:29 +08:00
|
|
|
return getDefaultSummary();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-18 03:53:04 +08:00
|
|
|
return getPersistentSummary(ResultEff, ReceiverEff, MayEscape);
|
2009-04-24 07:08:22 +08:00
|
|
|
}
|
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *
|
2011-01-25 08:03:53 +08:00
|
|
|
RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg,
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state,
|
2009-11-13 09:54:21 +08:00
|
|
|
const LocationContext *LC) {
|
|
|
|
|
|
|
|
// We need the type-information of the tracked receiver object
|
|
|
|
// Retrieve it from the state.
|
2011-01-25 08:03:53 +08:00
|
|
|
const Expr *Receiver = msg.getInstanceReceiver();
|
2011-08-13 07:37:29 +08:00
|
|
|
const ObjCInterfaceDecl *ID = 0;
|
2009-11-13 09:54:21 +08:00
|
|
|
|
|
|
|
// FIXME: Is this really working as expected? There are cases where
|
|
|
|
// we just use the 'ID' from the message expression.
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
SVal receiverV;
|
|
|
|
|
2010-05-22 05:56:53 +08:00
|
|
|
if (Receiver) {
|
2012-01-07 06:09:28 +08:00
|
|
|
receiverV = state->getSValAsScalarOrLoc(Receiver, LC);
|
2010-07-02 04:16:50 +08:00
|
|
|
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
// FIXME: Eventually replace the use of state->get<RefBindings> with
|
|
|
|
// a generic API for reasoning about the Objective-C types of symbolic
|
|
|
|
// objects.
|
|
|
|
if (SymbolRef Sym = receiverV.getAsLocSymbol())
|
|
|
|
if (const RefVal *T = state->get<RefBindings>(Sym))
|
2010-07-02 04:16:50 +08:00
|
|
|
if (const ObjCObjectPointerType* PT =
|
2009-11-13 09:54:21 +08:00
|
|
|
T->getType()->getAs<ObjCObjectPointerType>())
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
ID = PT->getInterfaceDecl();
|
2010-07-02 04:16:50 +08:00
|
|
|
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
// FIXME: this is a hack. This may or may not be the actual method
|
|
|
|
// that is called.
|
|
|
|
if (!ID) {
|
|
|
|
if (const ObjCObjectPointerType *PT =
|
|
|
|
Receiver->getType()->getAs<ObjCObjectPointerType>())
|
|
|
|
ID = PT->getInterfaceDecl();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// FIXME: Hack for 'super'.
|
2011-01-25 08:03:53 +08:00
|
|
|
ID = msg.getReceiverInterface();
|
2009-11-13 09:54:21 +08:00
|
|
|
}
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
|
2009-11-13 09:54:21 +08:00
|
|
|
// FIXME: The receiver could be a reference to a class, meaning that
|
|
|
|
// we should use the class method.
|
2011-08-23 12:27:15 +08:00
|
|
|
return getInstanceMethodSummary(msg, ID);
|
2009-11-13 09:54:21 +08:00
|
|
|
}
|
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *
|
2012-03-18 05:13:07 +08:00
|
|
|
RetainSummaryManager::getMethodSummary(Selector S, IdentifierInfo *ClsName,
|
|
|
|
const ObjCInterfaceDecl *ID,
|
|
|
|
const ObjCMethodDecl *MD, QualType RetTy,
|
|
|
|
ObjCMethodSummariesTy &CachedSummaries) {
|
2008-05-06 23:44:25 +08:00
|
|
|
|
2009-04-29 13:04:30 +08:00
|
|
|
// Look up a summary in our summary cache.
|
2012-03-18 05:13:07 +08:00
|
|
|
const RetainSummary *Summ = CachedSummaries.find(ID, ClsName, S);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
if (!Summ) {
|
2012-03-18 05:13:07 +08:00
|
|
|
Summ = getStandardMethodSummary(MD, S, RetTy);
|
2011-08-23 12:27:15 +08:00
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
// Annotations override defaults.
|
2011-08-23 12:27:15 +08:00
|
|
|
updateSummaryFromAnnotations(Summ, MD);
|
|
|
|
|
2009-07-22 07:27:57 +08:00
|
|
|
// Memoize the summary.
|
2012-03-18 05:13:07 +08:00
|
|
|
CachedSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ;
|
2009-07-22 07:27:57 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-24 03:11:35 +08:00
|
|
|
return Summ;
|
2008-05-07 05:26:51 +08:00
|
|
|
}
|
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
void RetainSummaryManager::InitializeClassMethodSummaries() {
|
2009-05-08 07:40:42 +08:00
|
|
|
assert(ScratchArgs.isEmpty());
|
2009-09-09 23:08:12 +08:00
|
|
|
// Create the [NSAssertionHandler currentHander] summary.
|
2009-10-16 06:25:12 +08:00
|
|
|
addClassMethSummary("NSAssertionHandler", "currentHandler",
|
2009-01-28 13:56:51 +08:00
|
|
|
getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC)));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-10-21 23:53:15 +08:00
|
|
|
// Create the [NSAutoreleasePool addObject:] summary.
|
2010-11-24 08:54:37 +08:00
|
|
|
ScratchArgs = AF.add(ScratchArgs, 0, Autorelease);
|
2009-10-16 06:25:12 +08:00
|
|
|
addClassMethSummary("NSAutoreleasePool", "addObject",
|
|
|
|
getPersistentSummary(RetEffect::MakeNoRet(),
|
|
|
|
DoNothing, Autorelease));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-25 01:50:11 +08:00
|
|
|
// Create the summaries for [NSObject performSelector...]. We treat
|
|
|
|
// these as 'stop tracking' for the arguments because they are often
|
|
|
|
// used for delegates that can release the object. When we have better
|
|
|
|
// inter-procedural analysis we can potentially do something better. This
|
|
|
|
// workaround is to remove false positives.
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *Summ =
|
2011-08-18 05:04:19 +08:00
|
|
|
getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking);
|
2009-04-25 01:50:11 +08:00
|
|
|
IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject");
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject",
|
|
|
|
"afterDelay", NULL);
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject",
|
|
|
|
"afterDelay", "inModes", NULL);
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread",
|
|
|
|
"withObject", "waitUntilDone", NULL);
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread",
|
|
|
|
"withObject", "waitUntilDone", "modes", NULL);
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread",
|
|
|
|
"withObject", "waitUntilDone", NULL);
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread",
|
|
|
|
"withObject", "waitUntilDone", "modes", NULL);
|
|
|
|
addClsMethSummary(NSObjectII, Summ, "performSelectorInBackground",
|
|
|
|
"withObject", NULL);
|
2008-05-06 08:30:21 +08:00
|
|
|
}
|
|
|
|
|
2008-06-24 06:21:20 +08:00
|
|
|
void RetainSummaryManager::InitializeMethodSummaries() {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
assert (ScratchArgs.isEmpty());
|
|
|
|
|
2008-05-07 05:26:51 +08:00
|
|
|
// Create the "init" selector. It just acts as a pass-through for the
|
|
|
|
// receiver.
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg);
|
2009-08-20 13:13:36 +08:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm);
|
|
|
|
|
|
|
|
// awakeAfterUsingCoder: behaves basically like an 'init' method. It
|
|
|
|
// claims the receiver and returns a retained object.
|
|
|
|
addNSObjectMethSummary(GetUnarySelector("awakeAfterUsingCoder", Ctx),
|
|
|
|
InitSumm);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-07 05:26:51 +08:00
|
|
|
// The next methods are allocators.
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE);
|
|
|
|
const RetainSummary *CFAllocSumm =
|
2009-08-29 03:52:12 +08:00
|
|
|
getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 10:26:56 +08:00
|
|
|
// Create the "retain" selector.
|
2011-08-22 03:41:36 +08:00
|
|
|
RetEffect NoRet = RetEffect::MakeNoRet();
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *Summ = getPersistentSummary(NoRet, IncRefMsg);
|
2008-06-26 05:21:56 +08:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-06 10:26:56 +08:00
|
|
|
// Create the "release" selector.
|
2011-08-22 03:41:36 +08:00
|
|
|
Summ = getPersistentSummary(NoRet, DecRefMsg);
|
2008-06-26 05:21:56 +08:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-05-08 05:17:39 +08:00
|
|
|
// Create the "drain" selector.
|
2011-08-22 03:41:36 +08:00
|
|
|
Summ = getPersistentSummary(NoRet, isGCEnabled() ? DoNothing : DecRef);
|
2008-06-26 05:21:56 +08:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-18 03:42:23 +08:00
|
|
|
// Create the -dealloc summary.
|
2011-08-22 03:41:36 +08:00
|
|
|
Summ = getPersistentSummary(NoRet, Dealloc);
|
2009-03-18 03:42:23 +08:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ);
|
2008-05-06 10:26:56 +08:00
|
|
|
|
|
|
|
// Create the "autorelease" selector.
|
2011-08-22 03:41:36 +08:00
|
|
|
Summ = getPersistentSummary(NoRet, Autorelease);
|
2008-06-26 05:21:56 +08:00
|
|
|
addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-24 01:45:03 +08:00
|
|
|
// Specially handle NSAutoreleasePool.
|
2009-02-25 10:54:57 +08:00
|
|
|
addInstMethSummary("NSAutoreleasePool", "init",
|
2011-08-22 03:41:36 +08:00
|
|
|
getPersistentSummary(NoRet, NewAutoreleasePool));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// For NSWindow, allocated objects are (initially) self-owned.
|
2009-02-23 10:51:29 +08: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>.
|
2009-03-05 07:30:42 +08:00
|
|
|
// See also http://llvm.org/bugs/show_bug.cgi?id=3714.
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *NoTrackYet = getPersistentSummary(RetEffect::MakeNoRet(),
|
2009-05-13 04:06:54 +08:00
|
|
|
StopTracking,
|
|
|
|
StopTracking);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-04 03:02:51 +08:00
|
|
|
addClassMethSummary("NSWindow", "alloc", NoTrackYet);
|
|
|
|
|
2009-03-05 07:30:42 +08:00
|
|
|
#if 0
|
2009-05-13 04:06:54 +08:00
|
|
|
addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect",
|
2008-08-13 02:48:50 +08:00
|
|
|
"styleMask", "backing", "defer", NULL);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-13 04:06:54 +08:00
|
|
|
addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect",
|
2008-08-13 02:48:50 +08:00
|
|
|
"styleMask", "backing", "defer", "screen", NULL);
|
2009-03-05 07:30:42 +08:00
|
|
|
#endif
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-07-02 01:21:27 +08:00
|
|
|
// For NSPanel (which subclasses NSWindow), allocated objects are not
|
|
|
|
// self-owned.
|
2009-04-04 03:02:51 +08:00
|
|
|
// FIXME: For now we don't track NSPanels. object for the same reason
|
|
|
|
// as for NSWindow objects.
|
|
|
|
addClassMethSummary("NSPanel", "alloc", NoTrackYet);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-13 04:06:54 +08:00
|
|
|
#if 0
|
|
|
|
addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect",
|
2008-08-13 02:48:50 +08:00
|
|
|
"styleMask", "backing", "defer", NULL);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-13 04:06:54 +08:00
|
|
|
addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect",
|
2008-08-13 02:48:50 +08:00
|
|
|
"styleMask", "backing", "defer", "screen", NULL);
|
2009-05-13 04:06:54 +08:00
|
|
|
#endif
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-19 07:14:34 +08:00
|
|
|
// Don't track allocated autorelease pools yet, as it is okay to prematurely
|
|
|
|
// exit a method.
|
|
|
|
addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet);
|
2012-02-19 05:37:48 +08:00
|
|
|
addClassMethSummary("NSAutoreleasePool", "allocWithZone", NoTrackYet, false);
|
2008-06-26 05:21:56 +08:00
|
|
|
|
2009-05-21 06:39:57 +08:00
|
|
|
// Create summaries QCRenderer/QCView -createSnapShotImageOfType:
|
|
|
|
addInstMethSummary("QCRenderer", AllocSumm,
|
|
|
|
"createSnapshotImageOfType", NULL);
|
|
|
|
addInstMethSummary("QCView", AllocSumm,
|
|
|
|
"createSnapshotImageOfType", NULL);
|
|
|
|
|
2009-06-16 04:58:58 +08:00
|
|
|
// Create summaries for CIContext, 'createCGImage' and
|
2009-08-29 03:52:12 +08:00
|
|
|
// 'createCGLayerWithSize'. These objects are CF objects, and are not
|
|
|
|
// automatically garbage collected.
|
|
|
|
addInstMethSummary("CIContext", CFAllocSumm,
|
2009-05-21 06:39:57 +08:00
|
|
|
"createCGImage", "fromRect", NULL);
|
2009-08-29 03:52:12 +08:00
|
|
|
addInstMethSummary("CIContext", CFAllocSumm,
|
2009-09-09 23:08:12 +08:00
|
|
|
"createCGImage", "fromRect", "format", "colorSpace", NULL);
|
2009-08-29 03:52:12 +08:00
|
|
|
addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize",
|
2009-06-16 04:58:58 +08:00
|
|
|
"info", NULL);
|
2008-05-06 08:38:54 +08:00
|
|
|
}
|
|
|
|
|
2008-10-21 23:53:15 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2009-02-25 03:15:11 +08:00
|
|
|
// AutoreleaseBindings - State used to track objects in autorelease pools.
|
2008-10-21 23:53:15 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2009-02-25 03:15:11 +08:00
|
|
|
typedef llvm::ImmutableMap<SymbolRef, unsigned> ARCounts;
|
|
|
|
typedef llvm::ImmutableMap<SymbolRef, ARCounts> ARPoolContents;
|
|
|
|
typedef llvm::ImmutableList<SymbolRef> ARStack;
|
2009-02-24 01:45:03 +08:00
|
|
|
|
2009-02-25 03:15:11 +08:00
|
|
|
static int AutoRCIndex = 0;
|
2008-10-21 23:53:15 +08:00
|
|
|
static int AutoRBIndex = 0;
|
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
namespace { class AutoreleasePoolContents {}; }
|
|
|
|
namespace { class AutoreleaseStack {}; }
|
2009-02-25 03:15:11 +08:00
|
|
|
|
2008-10-21 23:53:15 +08:00
|
|
|
namespace clang {
|
2010-12-23 15:20:52 +08:00
|
|
|
namespace ento {
|
2011-08-16 06:09:50 +08:00
|
|
|
template<> struct ProgramStateTrait<AutoreleaseStack>
|
|
|
|
: public ProgramStatePartialTrait<ARStack> {
|
2011-08-13 07:37:29 +08:00
|
|
|
static inline void *GDMIndex() { return &AutoRBIndex; }
|
2009-02-25 03:15:11 +08:00
|
|
|
};
|
|
|
|
|
2011-08-16 06:09:50 +08:00
|
|
|
template<> struct ProgramStateTrait<AutoreleasePoolContents>
|
|
|
|
: public ProgramStatePartialTrait<ARPoolContents> {
|
2011-08-13 07:37:29 +08:00
|
|
|
static inline void *GDMIndex() { return &AutoRCIndex; }
|
2009-02-25 03:15:11 +08:00
|
|
|
};
|
2010-12-23 02:53:20 +08:00
|
|
|
} // end GR namespace
|
2009-02-25 03:15:11 +08:00
|
|
|
} // end clang namespace
|
2008-10-21 23:53:15 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
static SymbolRef GetCurrentAutoreleasePool(ProgramStateRef state) {
|
2009-03-21 01:34:15 +08:00
|
|
|
ARStack stack = state->get<AutoreleaseStack>();
|
|
|
|
return stack.isEmpty() ? SymbolRef() : stack.getHead();
|
|
|
|
}
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
static ProgramStateRef
|
|
|
|
SendAutorelease(ProgramStateRef state,
|
2011-08-16 06:09:50 +08:00
|
|
|
ARCounts::Factory &F,
|
|
|
|
SymbolRef sym) {
|
2009-03-21 01:34:15 +08:00
|
|
|
SymbolRef pool = GetCurrentAutoreleasePool(state);
|
2009-06-18 09:23:53 +08:00
|
|
|
const ARCounts *cnts = state->get<AutoreleasePoolContents>(pool);
|
2009-03-21 01:34:15 +08:00
|
|
|
ARCounts newCnts(0);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-21 01:34:15 +08:00
|
|
|
if (cnts) {
|
|
|
|
const unsigned *cnt = (*cnts).lookup(sym);
|
2010-11-24 08:54:37 +08:00
|
|
|
newCnts = F.add(*cnts, sym, cnt ? *cnt + 1 : 1);
|
2009-03-21 01:34:15 +08:00
|
|
|
}
|
|
|
|
else
|
2010-11-24 08:54:37 +08:00
|
|
|
newCnts = F.add(F.getEmptyMap(), sym, 1);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
return state->set<AutoreleasePoolContents>(pool, newCnts);
|
2009-03-21 01:34:15 +08:00
|
|
|
}
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Error reporting.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
2011-08-24 04:55:48 +08:00
|
|
|
typedef llvm::DenseMap<const ExplodedNode *, const RetainSummary *>
|
|
|
|
SummaryLogTy;
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
//===-------------===//
|
|
|
|
// Bug Descriptions. //
|
2009-09-09 23:08:12 +08:00
|
|
|
//===-------------===//
|
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class CFRefBug : public BugType {
|
2009-04-30 02:50:19 +08:00
|
|
|
protected:
|
2011-08-24 13:47:39 +08:00
|
|
|
CFRefBug(StringRef name)
|
|
|
|
: BugType(name, "Memory (Core Foundation/Objective-C)") {}
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// FIXME: Eventually remove.
|
2011-08-24 13:47:39 +08:00
|
|
|
virtual const char *getDescription() const = 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
virtual bool isLeak() const { return false; }
|
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class UseAfterRelease : public CFRefBug {
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2011-08-24 13:47:39 +08:00
|
|
|
UseAfterRelease() : CFRefBug("Use-after-release") {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-24 13:47:39 +08:00
|
|
|
const char *getDescription() const {
|
2009-04-30 02:50:19 +08:00
|
|
|
return "Reference-counted object is used after it is released";
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class BadRelease : public CFRefBug {
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2011-08-24 13:47:39 +08:00
|
|
|
BadRelease() : CFRefBug("Bad release") {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-24 13:47:39 +08:00
|
|
|
const char *getDescription() const {
|
2009-10-02 01:31:50 +08:00
|
|
|
return "Incorrect decrement of the reference count of an object that is "
|
|
|
|
"not owned at this point by the caller";
|
2008-04-12 04:51:02 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class DeallocGC : public CFRefBug {
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2011-08-24 13:47:39 +08:00
|
|
|
DeallocGC()
|
|
|
|
: CFRefBug("-dealloc called while using garbage collection") {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
const char *getDescription() const {
|
2009-05-09 08:10:05 +08:00
|
|
|
return "-dealloc called while using garbage collection";
|
2008-05-06 10:41:27 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class DeallocNotOwned : public CFRefBug {
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2011-08-24 13:47:39 +08:00
|
|
|
DeallocNotOwned()
|
|
|
|
: CFRefBug("-dealloc sent to non-exclusively owned object") {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
const char *getDescription() const {
|
|
|
|
return "-dealloc sent to object that may be referenced elsewhere";
|
2008-03-12 09:21:45 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
};
|
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class OverAutorelease : public CFRefBug {
|
2009-05-09 08:10:05 +08:00
|
|
|
public:
|
2011-08-24 13:47:39 +08:00
|
|
|
OverAutorelease()
|
|
|
|
: CFRefBug("Object sent -autorelease too many times") {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 08:10:05 +08:00
|
|
|
const char *getDescription() const {
|
2009-05-10 13:11:21 +08:00
|
|
|
return "Object sent -autorelease too many times";
|
2009-05-09 08:10:05 +08:00
|
|
|
}
|
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class ReturnedNotOwnedForOwned : public CFRefBug {
|
2009-05-10 14:25:57 +08:00
|
|
|
public:
|
2011-08-24 13:47:39 +08:00
|
|
|
ReturnedNotOwnedForOwned()
|
|
|
|
: CFRefBug("Method should return an owned object") {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-10 14:25:57 +08:00
|
|
|
const char *getDescription() const {
|
2011-07-16 06:17:54 +08:00
|
|
|
return "Object with a +0 retain count returned to caller where a +1 "
|
2009-05-10 14:25:57 +08:00
|
|
|
"(owning) retain count is expected";
|
|
|
|
}
|
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class Leak : public CFRefBug {
|
2009-04-30 02:50:19 +08:00
|
|
|
const bool isReturn;
|
|
|
|
protected:
|
2011-08-24 13:47:39 +08:00
|
|
|
Leak(StringRef name, bool isRet)
|
2011-08-25 09:14:38 +08:00
|
|
|
: CFRefBug(name), isReturn(isRet) {
|
|
|
|
// Leaks should not be reported if they are post-dominated by a sink.
|
|
|
|
setSuppressOnSink(true);
|
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-24 13:47:39 +08:00
|
|
|
const char *getDescription() const { return ""; }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
bool isLeak() const { return true; }
|
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class LeakAtReturn : public Leak {
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2011-08-24 13:47:39 +08:00
|
|
|
LeakAtReturn(StringRef name)
|
|
|
|
: Leak(name, true) {}
|
2009-04-30 02:50:19 +08:00
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class LeakWithinFunction : public Leak {
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2011-08-24 13:47:39 +08:00
|
|
|
LeakWithinFunction(StringRef name)
|
|
|
|
: Leak(name, false) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
};
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
//===---------===//
|
|
|
|
// Bug Reports. //
|
|
|
|
//===---------===//
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-20 07:21:56 +08:00
|
|
|
class CFRefReportVisitor : public BugReporterVisitor {
|
2011-08-20 09:27:22 +08:00
|
|
|
protected:
|
2011-08-20 07:21:56 +08:00
|
|
|
SymbolRef Sym;
|
2011-08-24 04:55:48 +08:00
|
|
|
const SummaryLogTy &SummaryLog;
|
2011-08-24 13:47:39 +08:00
|
|
|
bool GCEnabled;
|
2011-08-20 09:27:22 +08:00
|
|
|
|
2011-08-20 07:21:56 +08:00
|
|
|
public:
|
2011-08-24 13:47:39 +08:00
|
|
|
CFRefReportVisitor(SymbolRef sym, bool gcEnabled, const SummaryLogTy &log)
|
|
|
|
: Sym(sym), SummaryLog(log), GCEnabled(gcEnabled) {}
|
2011-08-20 07:21:56 +08:00
|
|
|
|
2011-08-20 09:27:22 +08:00
|
|
|
virtual void Profile(llvm::FoldingSetNodeID &ID) const {
|
2011-08-20 07:21:56 +08:00
|
|
|
static int x = 0;
|
|
|
|
ID.AddPointer(&x);
|
|
|
|
ID.AddPointer(Sym);
|
|
|
|
}
|
|
|
|
|
2011-08-20 09:27:22 +08:00
|
|
|
virtual PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
|
|
|
|
const ExplodedNode *PrevN,
|
|
|
|
BugReporterContext &BRC,
|
|
|
|
BugReport &BR);
|
|
|
|
|
|
|
|
virtual PathDiagnosticPiece *getEndPath(BugReporterContext &BRC,
|
|
|
|
const ExplodedNode *N,
|
|
|
|
BugReport &BR);
|
|
|
|
};
|
|
|
|
|
|
|
|
class CFRefLeakReportVisitor : public CFRefReportVisitor {
|
|
|
|
public:
|
2011-08-24 13:47:39 +08:00
|
|
|
CFRefLeakReportVisitor(SymbolRef sym, bool GCEnabled,
|
2011-08-24 04:55:48 +08:00
|
|
|
const SummaryLogTy &log)
|
2011-08-24 13:47:39 +08:00
|
|
|
: CFRefReportVisitor(sym, GCEnabled, log) {}
|
2011-08-20 09:27:22 +08:00
|
|
|
|
|
|
|
PathDiagnosticPiece *getEndPath(BugReporterContext &BRC,
|
|
|
|
const ExplodedNode *N,
|
|
|
|
BugReport &BR);
|
2011-08-20 07:21:56 +08:00
|
|
|
};
|
|
|
|
|
2011-08-18 07:00:25 +08:00
|
|
|
class CFRefReport : public BugReport {
|
2011-08-25 06:39:09 +08:00
|
|
|
void addGCModeDescription(const LangOptions &LOpts, bool GCEnabled);
|
2011-08-24 13:47:39 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2011-08-25 06:39:09 +08:00
|
|
|
CFRefReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled,
|
|
|
|
const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
|
|
|
|
bool registerVisitor = true)
|
2011-08-23 02:54:07 +08:00
|
|
|
: BugReport(D, D.getDescription(), n) {
|
2011-08-20 09:27:22 +08:00
|
|
|
if (registerVisitor)
|
2011-08-25 06:39:09 +08:00
|
|
|
addVisitor(new CFRefReportVisitor(sym, GCEnabled, Log));
|
|
|
|
addGCModeDescription(LOpts, GCEnabled);
|
2011-08-20 07:21:56 +08:00
|
|
|
}
|
2009-05-10 13:11:21 +08:00
|
|
|
|
2011-08-25 06:39:09 +08:00
|
|
|
CFRefReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled,
|
|
|
|
const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
|
|
|
|
StringRef endText)
|
2011-08-23 02:54:07 +08:00
|
|
|
: BugReport(D, D.getDescription(), endText, n) {
|
2011-08-25 06:39:09 +08:00
|
|
|
addVisitor(new CFRefReportVisitor(sym, GCEnabled, Log));
|
|
|
|
addGCModeDescription(LOpts, GCEnabled);
|
2011-08-20 07:21:56 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-18 07:00:25 +08:00
|
|
|
virtual std::pair<ranges_iterator, ranges_iterator> getRanges() {
|
2011-08-23 02:54:07 +08:00
|
|
|
const CFRefBug& BugTy = static_cast<CFRefBug&>(getBugType());
|
|
|
|
if (!BugTy.isLeak())
|
2011-08-18 07:00:25 +08:00
|
|
|
return BugReport::getRanges();
|
2009-04-30 02:50:19 +08:00
|
|
|
else
|
2010-12-04 09:12:15 +08:00
|
|
|
return std::make_pair(ranges_iterator(), ranges_iterator());
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
|
|
|
};
|
2009-05-10 13:11:21 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class CFRefLeakReport : public CFRefReport {
|
2009-04-30 02:50:19 +08:00
|
|
|
const MemRegion* AllocBinding;
|
2011-08-20 09:27:22 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
public:
|
2011-08-25 06:39:09 +08:00
|
|
|
CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled,
|
|
|
|
const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
|
2011-10-26 03:57:11 +08:00
|
|
|
CheckerContext &Ctx);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-09-21 05:38:35 +08:00
|
|
|
PathDiagnosticLocation getLocation(const SourceManager &SM) const {
|
|
|
|
assert(Location.isValid());
|
|
|
|
return Location;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
};
|
2009-02-14 11:16:10 +08:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
2011-08-25 06:39:09 +08:00
|
|
|
void CFRefReport::addGCModeDescription(const LangOptions &LOpts,
|
|
|
|
bool GCEnabled) {
|
2011-08-25 04:38:42 +08:00
|
|
|
const char *GCModeDescription = 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-09-14 01:21:33 +08:00
|
|
|
switch (LOpts.getGC()) {
|
2011-08-23 04:31:28 +08:00
|
|
|
case LangOptions::GCOnly:
|
2011-08-25 06:39:09 +08:00
|
|
|
assert(GCEnabled);
|
2011-08-24 13:47:39 +08:00
|
|
|
GCModeDescription = "Code is compiled to only use garbage collection";
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-23 04:31:28 +08:00
|
|
|
case LangOptions::NonGC:
|
2011-08-25 06:39:09 +08:00
|
|
|
assert(!GCEnabled);
|
2011-08-24 13:47:39 +08:00
|
|
|
GCModeDescription = "Code is compiled to use reference counts";
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-23 04:31:28 +08:00
|
|
|
case LangOptions::HybridGC:
|
2011-08-25 06:39:09 +08:00
|
|
|
if (GCEnabled) {
|
2011-08-24 13:47:39 +08:00
|
|
|
GCModeDescription = "Code is compiled to use either garbage collection "
|
|
|
|
"(GC) or reference counts (non-GC). The bug occurs "
|
|
|
|
"with GC enabled";
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
GCModeDescription = "Code is compiled to use either garbage collection "
|
|
|
|
"(GC) or reference counts (non-GC). The bug occurs "
|
|
|
|
"in non-GC mode";
|
|
|
|
break;
|
2011-08-23 04:31:28 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2011-08-24 13:47:39 +08:00
|
|
|
|
2011-08-25 04:38:42 +08:00
|
|
|
assert(GCModeDescription && "invalid/unknown GC mode");
|
2011-08-24 13:47:39 +08:00
|
|
|
addExtraText(GCModeDescription);
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2008-04-12 06:25:11 +08:00
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
// FIXME: This should be a method on SmallVector.
|
2011-07-23 18:55:15 +08:00
|
|
|
static inline bool contains(const SmallVectorImpl<ArgEffect>& V,
|
2009-04-30 02:50:19 +08:00
|
|
|
ArgEffect X) {
|
2011-07-23 18:55:15 +08:00
|
|
|
for (SmallVectorImpl<ArgEffect>::const_iterator I=V.begin(), E=V.end();
|
2009-04-30 02:50:19 +08:00
|
|
|
I!=E; ++I)
|
|
|
|
if (*I == X) return true;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-11-15 05:59:21 +08:00
|
|
|
static bool isPropertyAccess(const Stmt *S, ParentMap &PM) {
|
|
|
|
unsigned maxDepth = 4;
|
|
|
|
while (S && maxDepth) {
|
|
|
|
if (const PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(S)) {
|
|
|
|
if (!isa<ObjCMessageExpr>(PO->getSyntacticForm()))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
S = PM.getParent(S);
|
|
|
|
--maxDepth;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-08-20 07:21:56 +08:00
|
|
|
PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
|
|
|
|
const ExplodedNode *PrevN,
|
|
|
|
BugReporterContext &BRC,
|
|
|
|
BugReport &BR) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-24 03:43:16 +08:00
|
|
|
if (!isa<StmtPoint>(N->getLocation()))
|
2009-05-13 15:12:33 +08:00
|
|
|
return NULL;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 05:39:49 +08:00
|
|
|
// Check if the type state has changed.
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef PrevSt = PrevN->getState();
|
|
|
|
ProgramStateRef CurrSt = N->getState();
|
2012-01-07 06:09:28 +08:00
|
|
|
const LocationContext *LCtx = N->getLocationContext();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
const RefVal* CurrT = CurrSt->get<RefBindings>(Sym);
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!CurrT) return NULL;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-18 09:23:53 +08:00
|
|
|
const RefVal &CurrV = *CurrT;
|
|
|
|
const RefVal *PrevT = PrevSt->get<RefBindings>(Sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08: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-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// This is the allocation site since the previous node had no bindings
|
|
|
|
// for this symbol.
|
|
|
|
if (!PrevT) {
|
2011-08-24 03:43:16 +08:00
|
|
|
const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-07 04:06:12 +08:00
|
|
|
if (isa<ObjCArrayLiteral>(S)) {
|
|
|
|
os << "NSArray literal is an object with a +0 retain count";
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2012-03-07 04:06:12 +08:00
|
|
|
else if (isa<ObjCDictionaryLiteral>(S)) {
|
|
|
|
os << "NSDictionary literal is an object with a +0 retain count";
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2012-03-07 04:06:12 +08:00
|
|
|
else {
|
|
|
|
if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
|
|
|
|
// Get the name of the callee (if it is available).
|
|
|
|
SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
|
|
|
|
if (const FunctionDecl *FD = X.getAsFunctionDecl())
|
|
|
|
os << "Call to function '" << *FD << '\'';
|
|
|
|
else
|
|
|
|
os << "function call";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
assert(isa<ObjCMessageExpr>(S));
|
|
|
|
// The message expression may have between written directly or as
|
|
|
|
// a property access. Lazily determine which case we are looking at.
|
|
|
|
os << (isPropertyAccess(S, N->getParentMap()) ? "Property" : "Method");
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-07 04:06:12 +08: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-09-09 23:08:12 +08:00
|
|
|
|
2012-03-07 04:06:12 +08:00
|
|
|
if (CurrV.isOwned()) {
|
|
|
|
os << "+1 retain count";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-07 04:06:12 +08:00
|
|
|
if (GCEnabled) {
|
|
|
|
assert(CurrV.getObjKind() == RetEffect::CF);
|
|
|
|
os << ". "
|
|
|
|
"Core Foundation objects are not automatically garbage collected.";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
assert (CurrV.isNotOwned());
|
|
|
|
os << "+0 retain count";
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2008-04-19 03:23:43 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-09-15 09:08:34 +08:00
|
|
|
PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
|
|
|
|
N->getLocationContext());
|
2009-04-30 02:50:19 +08:00
|
|
|
return new PathDiagnosticEventPiece(Pos, os.str());
|
2008-04-19 03:23:43 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Gather up the effects that were performed on the object at this
|
|
|
|
// program point
|
2011-07-23 18:55:15 +08:00
|
|
|
SmallVector<ArgEffect, 2> AEffects;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-24 04:55:48 +08:00
|
|
|
const ExplodedNode *OrigNode = BRC.getNodeResolver().getOriginalNode(N);
|
|
|
|
if (const RetainSummary *Summ = SummaryLog.lookup(OrigNode)) {
|
2009-04-30 02:50:19 +08:00
|
|
|
// We only have summaries attached to nodes after evaluating CallExpr and
|
|
|
|
// ObjCMessageExprs.
|
2011-08-24 03:43:16 +08:00
|
|
|
const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-23 06:35:28 +08:00
|
|
|
if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
|
2009-04-30 02:50:19 +08:00
|
|
|
// Iterate through the parameter expressions and see if the symbol
|
|
|
|
// was ever passed as an argument.
|
|
|
|
unsigned i = 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-23 06:35:28 +08:00
|
|
|
for (CallExpr::const_arg_iterator AI=CE->arg_begin(), AE=CE->arg_end();
|
2009-04-30 02:50:19 +08:00
|
|
|
AI!=AE; ++AI, ++i) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Retrieve the value of the argument. Is it the symbol
|
|
|
|
// we are interested in?
|
2012-01-07 06:09:28 +08:00
|
|
|
if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)
|
2009-04-30 02:50:19 +08:00
|
|
|
continue;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// We have an argument. Get the effect!
|
|
|
|
AEffects.push_back(Summ->getArg(i));
|
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
if (const Expr *receiver = ME->getInstanceReceiver())
|
2012-01-07 06:09:28 +08:00
|
|
|
if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
|
|
|
|
.getAsLocSymbol() == Sym) {
|
2009-04-30 02:50:19 +08:00
|
|
|
// The symbol we are tracking is the receiver.
|
|
|
|
AEffects.push_back(Summ->getReceiverEffect());
|
|
|
|
}
|
|
|
|
}
|
2009-02-19 02:54:33 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
do {
|
|
|
|
// Get the previous type state.
|
|
|
|
RefVal PrevV = *PrevT;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Specially handle -dealloc.
|
2011-08-24 13:47:39 +08:00
|
|
|
if (!GCEnabled && contains(AEffects, Dealloc)) {
|
2009-04-30 02:50:19 +08:00
|
|
|
// Determine if the object's reference count was pushed to zero.
|
|
|
|
assert(!(PrevV == CurrV) && "The typestate *must* have changed.");
|
|
|
|
// We may not have transitioned to 'release' if we hit an error.
|
|
|
|
// This case is handled elsewhere.
|
|
|
|
if (CurrV.getKind() == RefVal::Released) {
|
2009-05-09 04:01:42 +08:00
|
|
|
assert(CurrV.getCombinedCounts() == 0);
|
2009-04-30 02:50:19 +08:00
|
|
|
os << "Object released by directly sending the '-dealloc' message";
|
2009-03-18 03:42:23 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Specially handle CFMakeCollectable and friends.
|
|
|
|
if (contains(AEffects, MakeCollectable)) {
|
|
|
|
// Get the name of the function.
|
2011-08-24 03:43:16 +08:00
|
|
|
const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt();
|
2012-01-07 06:09:28 +08:00
|
|
|
SVal X =
|
|
|
|
CurrSt->getSValAsScalarOrLoc(cast<CallExpr>(S)->getCallee(), LCtx);
|
2011-08-13 07:37:29 +08:00
|
|
|
const FunctionDecl *FD = X.getAsFunctionDecl();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-24 13:47:39 +08:00
|
|
|
if (GCEnabled) {
|
2009-04-30 02:50:19 +08:00
|
|
|
// Determine if the object's reference count was pushed to zero.
|
|
|
|
assert(!(PrevV == CurrV) && "The typestate *must* have changed.");
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-10-15 02:45:37 +08:00
|
|
|
os << "In GC mode a call to '" << *FD
|
2009-04-30 02:50:19 +08:00
|
|
|
<< "' decrements an object's retain count and registers the "
|
|
|
|
"object with the garbage collector. ";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (CurrV.getKind() == RefVal::Released) {
|
|
|
|
assert(CurrV.getCount() == 0);
|
|
|
|
os << "Since it now has a 0 retain count the object can be "
|
|
|
|
"automatically collected by the garbage collector.";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
os << "An object must have a 0 retain count to be garbage collected. "
|
|
|
|
"After this call its retain count is +" << CurrV.getCount()
|
|
|
|
<< '.';
|
2008-05-23 01:31:13 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
else
|
2011-10-15 02:45:37 +08:00
|
|
|
os << "When GC is not enabled a call to '" << *FD
|
2009-04-30 02:50:19 +08:00
|
|
|
<< "' has no effect on its argument.";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Nothing more to say.
|
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Determine if the typestate has changed.
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!(PrevV == CurrV))
|
|
|
|
switch (CurrV.getKind()) {
|
2008-03-12 01:48:22 +08:00
|
|
|
case RefVal::Owned:
|
|
|
|
case RefVal::NotOwned:
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-09 04:01:42 +08:00
|
|
|
if (PrevV.getCount() == CurrV.getCount()) {
|
|
|
|
// Did an autorelease message get sent?
|
|
|
|
if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())
|
|
|
|
return 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-12 18:10:00 +08:00
|
|
|
assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
|
2009-05-10 13:11:21 +08:00
|
|
|
os << "Object sent -autorelease message";
|
2009-05-09 04:01:42 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (PrevV.getCount() > CurrV.getCount())
|
|
|
|
os << "Reference count decremented.";
|
|
|
|
else
|
|
|
|
os << "Reference count incremented.";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (unsigned Count = CurrV.getCount())
|
|
|
|
os << " The object now has a +" << Count << " retain count.";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (PrevV.getKind() == RefVal::Released) {
|
2011-08-24 13:47:39 +08:00
|
|
|
assert(GCEnabled && CurrV.getCount() > 0);
|
2012-03-17 13:49:15 +08:00
|
|
|
os << " The object is not eligible for garbage collection until "
|
|
|
|
"the retain count reaches 0 again.";
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-03-12 01:48:22 +08:00
|
|
|
case RefVal::Released:
|
2009-04-30 02:50:19 +08:00
|
|
|
os << "Object released.";
|
2008-03-12 01:48:22 +08:00
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case RefVal::ReturnedOwned:
|
2012-03-17 13:49:15 +08:00
|
|
|
// Autoreleases can be applied after marking a node ReturnedOwned.
|
|
|
|
if (CurrV.getAutoreleaseCount())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
os << "Object returned to caller as an owning reference (single "
|
|
|
|
"retain count transferred to caller)";
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
case RefVal::ReturnedNotOwned:
|
2011-05-27 02:45:44 +08:00
|
|
|
os << "Object returned to caller with a +0 retain count";
|
2009-04-30 02:50:19 +08:00
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Emit any remaining diagnostics for the argument effects (if any).
|
2011-07-23 18:55:15 +08:00
|
|
|
for (SmallVectorImpl<ArgEffect>::iterator I=AEffects.begin(),
|
2009-04-30 02:50:19 +08:00
|
|
|
E=AEffects.end(); I != E; ++I) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// A bunch of things have alternate behavior under GC.
|
2011-08-24 13:47:39 +08:00
|
|
|
if (GCEnabled)
|
2009-04-30 02:50:19 +08:00
|
|
|
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-09-09 23:08:12 +08:00
|
|
|
} while (0);
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (os.str().empty())
|
|
|
|
return 0; // We have nothing to say!
|
2009-05-13 15:12:33 +08:00
|
|
|
|
2011-08-24 03:43:16 +08:00
|
|
|
const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt();
|
2011-09-15 09:08:34 +08:00
|
|
|
PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
|
|
|
|
N->getLocationContext());
|
2011-08-13 07:37:29 +08:00
|
|
|
PathDiagnosticPiece *P = new PathDiagnosticEventPiece(Pos, os.str());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Add the range by scanning the children of the statement for any bindings
|
|
|
|
// to Sym.
|
2009-09-09 23:08:12 +08:00
|
|
|
for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end();
|
2009-07-23 06:35:28 +08:00
|
|
|
I!=E; ++I)
|
2011-08-13 07:37:29 +08:00
|
|
|
if (const Expr *Exp = dyn_cast_or_null<Expr>(*I))
|
2012-01-07 06:09:28 +08:00
|
|
|
if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {
|
2009-04-30 02:50:19 +08:00
|
|
|
P->addRange(Exp->getSourceRange());
|
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
return P;
|
|
|
|
}
|
|
|
|
|
2012-02-29 06:39:22 +08:00
|
|
|
// Find the first node in the current function context that referred to the
|
|
|
|
// tracked symbol and the memory location that value was stored to. Note, the
|
|
|
|
// value is only reported if the allocation occurred in the same function as
|
|
|
|
// the leak.
|
2009-08-06 09:32:16 +08:00
|
|
|
static std::pair<const ExplodedNode*,const MemRegion*>
|
2011-08-16 06:09:50 +08:00
|
|
|
GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
|
2009-04-30 02:50:19 +08:00
|
|
|
SymbolRef Sym) {
|
2011-08-13 07:37:29 +08:00
|
|
|
const ExplodedNode *Last = N;
|
2009-09-09 23:08:12 +08:00
|
|
|
const MemRegion* FirstBinding = 0;
|
2012-02-29 06:39:22 +08:00
|
|
|
const LocationContext *LeakContext = N->getLocationContext();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
while (N) {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef St = N->getState();
|
2009-04-30 02:50:19 +08:00
|
|
|
RefBindings B = St->get<RefBindings>();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (!B.lookup(Sym))
|
2008-04-11 07:44:06 +08:00
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-22 03:45:01 +08:00
|
|
|
StoreManager::FindUniqueBinding FB(Sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
StateMgr.iterBindings(St, FB);
|
|
|
|
if (FB) FirstBinding = FB.getRegion();
|
|
|
|
|
2012-02-29 06:39:22 +08:00
|
|
|
// Allocation node, is the last node in the current context in which the
|
|
|
|
// symbol was tracked.
|
|
|
|
if (N->getLocationContext() == LeakContext)
|
|
|
|
Last = N;
|
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
N = N->pred_empty() ? NULL : *(N->pred_begin());
|
2008-03-12 01:48:22 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-02-29 06:39:22 +08:00
|
|
|
// If allocation happened in a function different from the leak node context,
|
|
|
|
// do not report the binding.
|
|
|
|
if (N->getLocationContext() != LeakContext) {
|
|
|
|
FirstBinding = 0;
|
|
|
|
}
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
return std::make_pair(Last, FirstBinding);
|
2008-03-11 14:39:11 +08:00
|
|
|
}
|
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
PathDiagnosticPiece*
|
2011-08-20 09:27:22 +08:00
|
|
|
CFRefReportVisitor::getEndPath(BugReporterContext &BRC,
|
|
|
|
const ExplodedNode *EndN,
|
|
|
|
BugReport &BR) {
|
2012-03-09 09:13:14 +08:00
|
|
|
BR.markInteresting(Sym);
|
2011-08-20 09:27:22 +08:00
|
|
|
return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2008-04-09 09:10:13 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
PathDiagnosticPiece*
|
2011-08-20 09:27:22 +08:00
|
|
|
CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
|
|
|
|
const ExplodedNode *EndN,
|
|
|
|
BugReport &BR) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 05:39:49 +08:00
|
|
|
// Tell the BugReporterContext to report cases when the tracked symbol is
|
2009-04-30 02:50:19 +08:00
|
|
|
// assigned to different variables, etc.
|
2012-03-09 09:13:14 +08:00
|
|
|
BR.markInteresting(Sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// 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
|
|
|
|
// is stored to.
|
2011-08-13 07:37:29 +08:00
|
|
|
const ExplodedNode *AllocNode = 0;
|
2009-04-30 02:50:19 +08:00
|
|
|
const MemRegion* FirstBinding = 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
llvm::tie(AllocNode, FirstBinding) =
|
2009-05-09 07:32:51 +08:00
|
|
|
GetAllocationSite(BRC.getStateManager(), EndN, Sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-09-16 02:56:07 +08:00
|
|
|
SourceManager& SM = BRC.getSourceManager();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Compute an actual location for the leak. Sometimes a leak doesn't
|
|
|
|
// occur at an actual statement (e.g., transition between blocks; end
|
|
|
|
// of function) so we need to walk the graph and compute a real location.
|
2011-08-13 07:37:29 +08:00
|
|
|
const ExplodedNode *LeakN = EndN;
|
2011-09-16 02:56:07 +08:00
|
|
|
PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-05-27 02:45:44 +08:00
|
|
|
os << "Object leaked: ";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-05-27 02:45:44 +08:00
|
|
|
if (FirstBinding) {
|
|
|
|
os << "object allocated and stored into '"
|
|
|
|
<< FirstBinding->getString() << '\'';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
os << "allocated object";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Get the retain count.
|
|
|
|
const RefVal* RV = EndN->getState()->get<RefBindings>(Sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
if (RV->getKind() == RefVal::ErrorLeakReturned) {
|
|
|
|
// FIXME: Per comments in rdar://6320065, "create" only applies to CF
|
2011-07-16 06:17:54 +08:00
|
|
|
// objects. Only "copy", "alloc", "retain" and "new" transfer ownership
|
2009-04-30 02:50:19 +08:00
|
|
|
// to the caller for NS objects.
|
2011-05-25 14:19:45 +08:00
|
|
|
const Decl *D = &EndN->getCodeDecl();
|
|
|
|
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
|
|
|
|
os << " is returned from a method whose name ('"
|
|
|
|
<< MD->getSelector().getAsString()
|
|
|
|
<< "') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'."
|
2011-07-16 06:17:54 +08:00
|
|
|
" This violates the naming convention rules"
|
2011-05-27 02:45:44 +08:00
|
|
|
" given in the Memory Management Guide for Cocoa";
|
2011-05-25 14:19:45 +08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
const FunctionDecl *FD = cast<FunctionDecl>(D);
|
2011-12-22 14:35:52 +08:00
|
|
|
os << " is returned from a function whose name ('"
|
2012-02-07 19:57:57 +08:00
|
|
|
<< *FD
|
2011-05-25 14:19:45 +08:00
|
|
|
<< "') does not contain 'Copy' or 'Create'. This violates the naming"
|
2011-12-22 14:35:52 +08:00
|
|
|
" convention rules given in the Memory Management Guide for Core"
|
2011-05-27 02:45:44 +08:00
|
|
|
" Foundation";
|
2011-05-25 14:19:45 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
2009-05-10 14:25:57 +08:00
|
|
|
else if (RV->getKind() == RefVal::ErrorGCLeakReturned) {
|
2011-08-13 07:37:29 +08:00
|
|
|
ObjCMethodDecl &MD = cast<ObjCMethodDecl>(EndN->getCodeDecl());
|
2009-05-10 14:25:57 +08:00
|
|
|
os << " and returned from method '" << MD.getSelector().getAsString()
|
2009-05-11 00:52:15 +08:00
|
|
|
<< "' is potentially leaked when using garbage collection. Callers "
|
|
|
|
"of this method do not expect a returned object with a +1 retain "
|
|
|
|
"count since they expect the object to be managed by the garbage "
|
|
|
|
"collector";
|
2009-05-10 14:25:57 +08:00
|
|
|
}
|
2009-04-30 02:50:19 +08:00
|
|
|
else
|
2010-10-16 06:50:23 +08:00
|
|
|
os << " is not referenced later in this execution path and has a retain "
|
2011-05-27 02:45:44 +08:00
|
|
|
"count of +" << RV->getCount();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
return new PathDiagnosticEventPiece(L, os.str());
|
|
|
|
}
|
2009-02-05 07:49:09 +08:00
|
|
|
|
2011-08-25 06:39:09 +08:00
|
|
|
CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
|
|
|
|
bool GCEnabled, const SummaryLogTy &Log,
|
|
|
|
ExplodedNode *n, SymbolRef sym,
|
2011-10-26 03:57:11 +08:00
|
|
|
CheckerContext &Ctx)
|
2011-08-25 06:39:09 +08:00
|
|
|
: CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-04-15 13:22:18 +08:00
|
|
|
// Most bug reports are cached at the location where they occurred.
|
2009-04-30 02:50:19 +08:00
|
|
|
// With leaks, we want to unique them by the location where they were
|
|
|
|
// 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.
|
2011-08-13 07:37:29 +08:00
|
|
|
const ExplodedNode *AllocNode = 0;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-10-26 03:57:11 +08:00
|
|
|
const SourceManager& SMgr = Ctx.getSourceManager();
|
2011-09-21 05:38:35 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding.
|
2011-10-26 03:57:11 +08:00
|
|
|
GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// Get the SourceLocation for the allocation site.
|
|
|
|
ProgramPoint P = AllocNode->getLocation();
|
2011-09-21 05:38:35 +08:00
|
|
|
const Stmt *AllocStmt = cast<PostStmt>(P).getStmt();
|
|
|
|
Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
|
|
|
|
n->getLocationContext());
|
2009-04-30 02:50:19 +08:00
|
|
|
// Fill in the description of the bug.
|
|
|
|
Description.clear();
|
|
|
|
llvm::raw_string_ostream os(Description);
|
2009-05-03 03:05:19 +08:00
|
|
|
os << "Potential leak ";
|
2011-08-25 06:39:09 +08:00
|
|
|
if (GCEnabled)
|
2009-05-03 03:05:19 +08:00
|
|
|
os << "(when using garbage collection) ";
|
2012-02-29 05:49:08 +08:00
|
|
|
os << "of an object";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-30 02:50:19 +08:00
|
|
|
// FIXME: AllocBinding doesn't get populated for RegionStore yet.
|
|
|
|
if (AllocBinding)
|
2012-02-29 05:49:08 +08:00
|
|
|
os << " stored into '" << AllocBinding->getString() << '\'';
|
2011-08-20 07:21:56 +08:00
|
|
|
|
2011-08-25 06:39:09 +08:00
|
|
|
addVisitor(new CFRefLeakReportVisitor(sym, GCEnabled, Log));
|
2009-04-30 02:50:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Main checker logic.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2009-11-26 06:17:44 +08:00
|
|
|
namespace {
|
2011-09-02 14:44:22 +08:00
|
|
|
class RetainCountChecker
|
2011-08-25 02:56:32 +08:00
|
|
|
: public Checker< check::Bind,
|
2011-08-24 03:01:07 +08:00
|
|
|
check::DeadSymbols,
|
2011-08-25 02:56:32 +08:00
|
|
|
check::EndAnalysis,
|
2011-08-24 03:01:07 +08:00
|
|
|
check::EndPath,
|
2011-08-18 05:27:39 +08:00
|
|
|
check::PostStmt<BlockExpr>,
|
2011-06-16 07:02:42 +08:00
|
|
|
check::PostStmt<CastExpr>,
|
2011-08-23 07:48:23 +08:00
|
|
|
check::PostStmt<CallExpr>,
|
2011-08-28 06:51:26 +08:00
|
|
|
check::PostStmt<CXXConstructExpr>,
|
2012-03-07 04:06:12 +08:00
|
|
|
check::PostStmt<ObjCArrayLiteral>,
|
|
|
|
check::PostStmt<ObjCDictionaryLiteral>,
|
2011-08-23 07:48:23 +08:00
|
|
|
check::PostObjCMessage,
|
2011-08-24 03:43:16 +08:00
|
|
|
check::PreStmt<ReturnStmt>,
|
2011-08-18 05:27:39 +08:00
|
|
|
check::RegionChanges,
|
2011-08-22 05:58:18 +08:00
|
|
|
eval::Assume,
|
|
|
|
eval::Call > {
|
2012-02-05 10:12:40 +08:00
|
|
|
mutable OwningPtr<CFRefBug> useAfterRelease, releaseNotOwned;
|
|
|
|
mutable OwningPtr<CFRefBug> deallocGC, deallocNotOwned;
|
|
|
|
mutable OwningPtr<CFRefBug> overAutorelease, returnNotOwnedForOwned;
|
|
|
|
mutable OwningPtr<CFRefBug> leakWithinFunction, leakAtReturn;
|
|
|
|
mutable OwningPtr<CFRefBug> leakWithinFunctionGC, leakAtReturnGC;
|
2011-08-24 03:01:07 +08:00
|
|
|
|
|
|
|
typedef llvm::DenseMap<SymbolRef, const SimpleProgramPointTag *> SymbolTagMap;
|
|
|
|
|
|
|
|
// This map is only used to ensure proper deletion of any allocated tags.
|
|
|
|
mutable SymbolTagMap DeadSymbolTags;
|
|
|
|
|
2012-02-05 10:12:40 +08:00
|
|
|
mutable OwningPtr<RetainSummaryManager> Summaries;
|
|
|
|
mutable OwningPtr<RetainSummaryManager> SummariesGC;
|
2011-08-25 08:10:37 +08:00
|
|
|
|
2011-08-24 04:27:16 +08:00
|
|
|
mutable ARCounts::Factory ARCountFactory;
|
|
|
|
|
2011-08-25 02:56:32 +08:00
|
|
|
mutable SummaryLogTy SummaryLog;
|
|
|
|
mutable bool ShouldResetSummaryLog;
|
|
|
|
|
2011-08-21 05:17:59 +08:00
|
|
|
public:
|
2011-09-02 14:44:22 +08:00
|
|
|
RetainCountChecker() : ShouldResetSummaryLog(false) {}
|
2011-08-24 03:01:07 +08:00
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
virtual ~RetainCountChecker() {
|
2011-08-24 03:01:07 +08:00
|
|
|
DeleteContainerSeconds(DeadSymbolTags);
|
|
|
|
}
|
|
|
|
|
2011-08-25 02:56:32 +08:00
|
|
|
void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
|
|
|
|
ExprEngine &Eng) const {
|
|
|
|
// FIXME: This is a hack to make sure the summary log gets cleared between
|
|
|
|
// analyses of different code bodies.
|
|
|
|
//
|
|
|
|
// Why is this necessary? Because a checker's lifetime is tied to a
|
|
|
|
// translation unit, but an ExplodedGraph's lifetime is just a code body.
|
|
|
|
// Once in a blue moon, a new ExplodedNode will have the same address as an
|
|
|
|
// old one with an associated summary, and the bug report visitor gets very
|
|
|
|
// confused. (To make things worse, the summary lifetime is currently also
|
|
|
|
// tied to a code body, so we get a crash instead of incorrect results.)
|
2011-08-24 17:27:24 +08:00
|
|
|
//
|
|
|
|
// Why is this a bad solution? Because if the lifetime of the ExplodedGraph
|
|
|
|
// changes, things will start going wrong again. Really the lifetime of this
|
|
|
|
// log needs to be tied to either the specific nodes in it or the entire
|
|
|
|
// ExplodedGraph, not to a specific part of the code being analyzed.
|
|
|
|
//
|
2011-08-25 02:56:32 +08:00
|
|
|
// (Also, having stateful local data means that the same checker can't be
|
|
|
|
// used from multiple threads, but a lot of checkers have incorrect
|
|
|
|
// assumptions about that anyway. So that wasn't a priority at the time of
|
|
|
|
// this fix.)
|
2011-08-24 17:27:24 +08:00
|
|
|
//
|
2011-08-25 02:56:32 +08:00
|
|
|
// This happens at the end of analysis, but bug reports are emitted /after/
|
|
|
|
// this point. So we can't just clear the summary log now. Instead, we mark
|
|
|
|
// that the next time we access the summary log, it should be cleared.
|
|
|
|
|
|
|
|
// If we never reset the summary log during /this/ code body analysis,
|
|
|
|
// there were no new summaries. There might still have been summaries from
|
|
|
|
// the /last/ analysis, so clear them out to make sure the bug report
|
|
|
|
// visitors don't get confused.
|
|
|
|
if (ShouldResetSummaryLog)
|
|
|
|
SummaryLog.clear();
|
|
|
|
|
|
|
|
ShouldResetSummaryLog = !SummaryLog.empty();
|
2011-08-24 17:27:24 +08:00
|
|
|
}
|
|
|
|
|
2011-09-02 13:55:19 +08:00
|
|
|
CFRefBug *getLeakWithinFunctionBug(const LangOptions &LOpts,
|
|
|
|
bool GCEnabled) const {
|
|
|
|
if (GCEnabled) {
|
2011-08-25 09:14:38 +08:00
|
|
|
if (!leakWithinFunctionGC)
|
|
|
|
leakWithinFunctionGC.reset(new LeakWithinFunction("Leak of object when "
|
|
|
|
"using garbage "
|
|
|
|
"collection"));
|
2011-09-02 13:55:19 +08:00
|
|
|
return leakWithinFunctionGC.get();
|
2011-08-25 09:14:38 +08:00
|
|
|
} else {
|
|
|
|
if (!leakWithinFunction) {
|
2011-09-14 01:21:33 +08:00
|
|
|
if (LOpts.getGC() == LangOptions::HybridGC) {
|
2011-08-25 09:14:38 +08:00
|
|
|
leakWithinFunction.reset(new LeakWithinFunction("Leak of object when "
|
|
|
|
"not using garbage "
|
|
|
|
"collection (GC) in "
|
|
|
|
"dual GC/non-GC "
|
|
|
|
"code"));
|
|
|
|
} else {
|
|
|
|
leakWithinFunction.reset(new LeakWithinFunction("Leak"));
|
|
|
|
}
|
|
|
|
}
|
2011-09-02 13:55:19 +08:00
|
|
|
return leakWithinFunction.get();
|
2011-08-25 09:14:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-02 13:55:19 +08:00
|
|
|
CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts, bool GCEnabled) const {
|
|
|
|
if (GCEnabled) {
|
2011-08-25 09:14:38 +08:00
|
|
|
if (!leakAtReturnGC)
|
|
|
|
leakAtReturnGC.reset(new LeakAtReturn("Leak of returned object when "
|
|
|
|
"using garbage collection"));
|
2011-09-02 13:55:19 +08:00
|
|
|
return leakAtReturnGC.get();
|
2011-08-25 09:14:38 +08:00
|
|
|
} else {
|
|
|
|
if (!leakAtReturn) {
|
2011-09-14 01:21:33 +08:00
|
|
|
if (LOpts.getGC() == LangOptions::HybridGC) {
|
2011-08-25 09:14:38 +08:00
|
|
|
leakAtReturn.reset(new LeakAtReturn("Leak of returned object when "
|
|
|
|
"not using garbage collection "
|
|
|
|
"(GC) in dual GC/non-GC code"));
|
|
|
|
} else {
|
|
|
|
leakAtReturn.reset(new LeakAtReturn("Leak of returned object"));
|
|
|
|
}
|
|
|
|
}
|
2011-09-02 13:55:19 +08:00
|
|
|
return leakAtReturn.get();
|
2011-08-25 09:14:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-02 13:55:19 +08:00
|
|
|
RetainSummaryManager &getSummaryManager(ASTContext &Ctx,
|
|
|
|
bool GCEnabled) const {
|
|
|
|
// FIXME: We don't support ARC being turned on and off during one analysis.
|
|
|
|
// (nor, for that matter, do we support changing ASTContexts)
|
2012-03-11 15:00:24 +08:00
|
|
|
bool ARCEnabled = (bool)Ctx.getLangOpts().ObjCAutoRefCount;
|
2011-09-02 13:55:19 +08:00
|
|
|
if (GCEnabled) {
|
|
|
|
if (!SummariesGC)
|
2011-08-25 08:10:37 +08:00
|
|
|
SummariesGC.reset(new RetainSummaryManager(Ctx, true, ARCEnabled));
|
2011-09-02 13:55:19 +08:00
|
|
|
else
|
|
|
|
assert(SummariesGC->isARCEnabled() == ARCEnabled);
|
2011-08-25 08:10:37 +08:00
|
|
|
return *SummariesGC;
|
|
|
|
} else {
|
2011-09-02 13:55:19 +08:00
|
|
|
if (!Summaries)
|
2011-08-25 08:10:37 +08:00
|
|
|
Summaries.reset(new RetainSummaryManager(Ctx, false, ARCEnabled));
|
2011-09-02 13:55:19 +08:00
|
|
|
else
|
|
|
|
assert(Summaries->isARCEnabled() == ARCEnabled);
|
2011-08-25 08:10:37 +08:00
|
|
|
return *Summaries;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-02 13:55:19 +08:00
|
|
|
RetainSummaryManager &getSummaryManager(CheckerContext &C) const {
|
|
|
|
return getSummaryManager(C.getASTContext(), C.isObjCGCEnabled());
|
|
|
|
}
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
void printState(raw_ostream &Out, ProgramStateRef State,
|
2011-08-29 03:11:56 +08:00
|
|
|
const char *NL, const char *Sep) const;
|
|
|
|
|
2011-10-06 08:43:15 +08:00
|
|
|
void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
|
2011-08-21 05:16:58 +08:00
|
|
|
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
|
|
|
|
void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
|
2011-06-16 07:02:42 +08:00
|
|
|
|
2011-08-23 07:48:23 +08:00
|
|
|
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
|
2011-08-28 06:51:26 +08:00
|
|
|
void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const;
|
2012-03-07 04:06:12 +08:00
|
|
|
void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const;
|
|
|
|
void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;
|
2011-08-23 07:48:23 +08:00
|
|
|
void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const;
|
2012-03-07 04:06:12 +08:00
|
|
|
|
2011-08-23 07:48:23 +08:00
|
|
|
void checkSummary(const RetainSummary &Summ, const CallOrObjCMessage &Call,
|
2011-08-28 13:16:28 +08:00
|
|
|
CheckerContext &C) const;
|
2011-08-23 07:48:23 +08:00
|
|
|
|
2011-08-22 05:58:18 +08:00
|
|
|
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
|
2011-08-21 05:16:58 +08:00
|
|
|
bool Assumption) const;
|
2011-08-18 05:27:39 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef
|
|
|
|
checkRegionChanges(ProgramStateRef state,
|
2011-08-28 06:51:26 +08:00
|
|
|
const StoreManager::InvalidatedSymbols *invalidated,
|
|
|
|
ArrayRef<const MemRegion *> ExplicitRegions,
|
2012-02-15 05:55:24 +08:00
|
|
|
ArrayRef<const MemRegion *> Regions,
|
|
|
|
const CallOrObjCMessage *Call) const;
|
2011-08-21 05:16:58 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
bool wantsRegionChangeUpdate(ProgramStateRef state) const {
|
2011-08-21 05:17:59 +08:00
|
|
|
return true;
|
2011-08-21 05:16:58 +08:00
|
|
|
}
|
2011-08-23 07:48:23 +08:00
|
|
|
|
2011-08-24 03:43:16 +08:00
|
|
|
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
|
|
|
|
void checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C,
|
|
|
|
ExplodedNode *Pred, RetEffect RE, RefVal X,
|
2012-01-27 05:29:00 +08:00
|
|
|
SymbolRef Sym, ProgramStateRef state) const;
|
2011-08-24 03:43:16 +08:00
|
|
|
|
2011-08-24 03:01:07 +08:00
|
|
|
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
|
2011-10-26 03:56:48 +08:00
|
|
|
void checkEndPath(CheckerContext &C) const;
|
2011-08-24 03:01:07 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef updateSymbol(ProgramStateRef state, SymbolRef sym,
|
2011-09-02 13:55:19 +08:00
|
|
|
RefVal V, ArgEffect E, RefVal::Kind &hasErr,
|
|
|
|
CheckerContext &C) const;
|
2011-08-24 04:27:16 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange,
|
2011-08-23 07:48:23 +08:00
|
|
|
RefVal::Kind ErrorKind, SymbolRef Sym,
|
|
|
|
CheckerContext &C) const;
|
2012-03-07 04:06:12 +08:00
|
|
|
|
|
|
|
void processObjCLiterals(CheckerContext &C, const Expr *Ex) const;
|
2011-08-23 07:48:23 +08:00
|
|
|
|
2011-08-24 03:01:07 +08:00
|
|
|
const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const;
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef handleSymbolDeath(ProgramStateRef state,
|
2011-08-24 03:01:07 +08:00
|
|
|
SymbolRef sid, RefVal V,
|
|
|
|
SmallVectorImpl<SymbolRef> &Leaked) const;
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
std::pair<ExplodedNode *, ProgramStateRef >
|
|
|
|
handleAutoreleaseCounts(ProgramStateRef state,
|
2011-08-24 04:07:14 +08:00
|
|
|
GenericNodeBuilderRefCount Bd, ExplodedNode *Pred,
|
2011-10-26 03:57:11 +08:00
|
|
|
CheckerContext &Ctx, SymbolRef Sym, RefVal V) const;
|
2011-08-24 04:07:14 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ExplodedNode *processLeaks(ProgramStateRef state,
|
2011-08-24 03:01:07 +08:00
|
|
|
SmallVectorImpl<SymbolRef> &Leaked,
|
|
|
|
GenericNodeBuilderRefCount &Builder,
|
2011-10-26 03:57:11 +08:00
|
|
|
CheckerContext &Ctx,
|
2011-08-24 03:01:07 +08:00
|
|
|
ExplodedNode *Pred = 0) const;
|
2009-11-26 06:17:44 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2011-08-18 05:27:39 +08:00
|
|
|
namespace {
|
|
|
|
class StopTrackingCallback : public SymbolVisitor {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state;
|
2011-08-18 05:27:39 +08:00
|
|
|
public:
|
2012-01-27 05:29:00 +08:00
|
|
|
StopTrackingCallback(ProgramStateRef st) : state(st) {}
|
|
|
|
ProgramStateRef getState() const { return state; }
|
2011-08-18 05:27:39 +08:00
|
|
|
|
|
|
|
bool VisitSymbol(SymbolRef sym) {
|
|
|
|
state = state->remove<RefBindings>(sym);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Handle statements that may have an effect on refcounts.
|
|
|
|
//===----------------------------------------------------------------------===//
|
2011-08-18 05:27:39 +08:00
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
void RetainCountChecker::checkPostStmt(const BlockExpr *BE,
|
|
|
|
CheckerContext &C) const {
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
// Scan the BlockDecRefExprs for any object the retain count checker
|
2010-07-02 04:16:50 +08:00
|
|
|
// may be tracking.
|
2011-02-02 21:00:07 +08:00
|
|
|
if (!BE->getBlockDecl()->hasCaptures())
|
2009-11-26 10:38:19 +08:00
|
|
|
return;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2009-11-26 10:38:19 +08:00
|
|
|
const BlockDataRegion *R =
|
2012-01-07 06:09:28 +08:00
|
|
|
cast<BlockDataRegion>(state->getSVal(BE,
|
|
|
|
C.getLocationContext()).getAsRegion());
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 10:38:19 +08:00
|
|
|
BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
|
|
|
|
E = R->referenced_vars_end();
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-11-26 10:38:19 +08:00
|
|
|
if (I == E)
|
|
|
|
return;
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-12-08 06:05:27 +08:00
|
|
|
// FIXME: For now we invalidate the tracking of all symbols passed to blocks
|
|
|
|
// via captured variables, even though captured variables result in a copy
|
|
|
|
// and in implicit increment/decrement of a retain count.
|
2011-07-23 18:55:15 +08:00
|
|
|
SmallVector<const MemRegion*, 10> Regions;
|
2011-10-27 05:06:44 +08:00
|
|
|
const LocationContext *LC = C.getLocationContext();
|
2010-12-02 15:49:45 +08:00
|
|
|
MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-12-08 06:05:27 +08:00
|
|
|
for ( ; I != E; ++I) {
|
|
|
|
const VarRegion *VR = *I;
|
|
|
|
if (VR->getSuperRegion() == R) {
|
|
|
|
VR = MemMgr.getVarRegion(VR->getDecl(), LC);
|
|
|
|
}
|
|
|
|
Regions.push_back(VR);
|
|
|
|
}
|
2010-07-02 04:16:50 +08:00
|
|
|
|
2009-12-08 06:05:27 +08:00
|
|
|
state =
|
|
|
|
state->scanReachableSymbols<StopTrackingCallback>(Regions.data(),
|
|
|
|
Regions.data() + Regions.size()).getState();
|
2011-10-27 05:06:34 +08:00
|
|
|
C.addTransition(state);
|
2009-11-26 10:38:19 +08:00
|
|
|
}
|
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
void RetainCountChecker::checkPostStmt(const CastExpr *CE,
|
|
|
|
CheckerContext &C) const {
|
2011-06-16 07:02:42 +08:00
|
|
|
const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE);
|
|
|
|
if (!BE)
|
|
|
|
return;
|
|
|
|
|
2011-06-17 14:50:50 +08:00
|
|
|
ArgEffect AE = IncRef;
|
2011-06-16 07:02:42 +08:00
|
|
|
|
|
|
|
switch (BE->getBridgeKind()) {
|
|
|
|
case clang::OBC_Bridge:
|
|
|
|
// Do nothing.
|
|
|
|
return;
|
|
|
|
case clang::OBC_BridgeRetained:
|
|
|
|
AE = IncRef;
|
|
|
|
break;
|
|
|
|
case clang::OBC_BridgeTransfer:
|
|
|
|
AE = DecRefBridgedTransfered;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2012-01-07 06:09:28 +08:00
|
|
|
SymbolRef Sym = state->getSVal(CE, C.getLocationContext()).getAsLocSymbol();
|
2011-06-16 07:02:42 +08:00
|
|
|
if (!Sym)
|
|
|
|
return;
|
|
|
|
const RefVal* T = state->get<RefBindings>(Sym);
|
|
|
|
if (!T)
|
|
|
|
return;
|
|
|
|
|
|
|
|
RefVal::Kind hasErr = (RefVal::Kind) 0;
|
2011-09-02 13:55:19 +08:00
|
|
|
state = updateSymbol(state, Sym, *T, AE, hasErr, C);
|
2011-06-16 07:02:42 +08:00
|
|
|
|
|
|
|
if (hasErr) {
|
2011-08-24 04:27:16 +08:00
|
|
|
// FIXME: If we get an error during a bridge cast, should we report it?
|
|
|
|
// Should we assert that there is no error?
|
2011-06-16 07:02:42 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-10-27 05:06:34 +08:00
|
|
|
C.addTransition(state);
|
2011-06-16 07:02:42 +08:00
|
|
|
}
|
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
void RetainCountChecker::checkPostStmt(const CallExpr *CE,
|
|
|
|
CheckerContext &C) const {
|
2011-08-23 07:48:23 +08:00
|
|
|
// Get the callee.
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2011-08-23 07:48:23 +08:00
|
|
|
const Expr *Callee = CE->getCallee();
|
2012-01-07 06:09:28 +08:00
|
|
|
SVal L = state->getSVal(Callee, C.getLocationContext());
|
2011-08-23 07:48:23 +08:00
|
|
|
|
2011-09-02 13:55:19 +08:00
|
|
|
RetainSummaryManager &Summaries = getSummaryManager(C);
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *Summ = 0;
|
2011-08-23 07:48:23 +08:00
|
|
|
|
|
|
|
// FIXME: Better support for blocks. For now we stop tracking anything
|
|
|
|
// that is passed to blocks.
|
|
|
|
// FIXME: Need to handle variables that are "captured" by the block.
|
|
|
|
if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) {
|
|
|
|
Summ = Summaries.getPersistentStopSummary();
|
|
|
|
} else if (const FunctionDecl *FD = L.getAsFunctionDecl()) {
|
|
|
|
Summ = Summaries.getSummary(FD);
|
|
|
|
} else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) {
|
|
|
|
if (const CXXMethodDecl *MD = me->getMethodDecl())
|
|
|
|
Summ = Summaries.getSummary(MD);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Summ)
|
2011-10-06 07:54:29 +08:00
|
|
|
Summ = Summaries.getDefaultSummary();
|
2011-08-23 07:48:23 +08:00
|
|
|
|
2012-01-07 06:09:28 +08:00
|
|
|
checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C);
|
2011-08-23 07:48:23 +08:00
|
|
|
}
|
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
void RetainCountChecker::checkPostStmt(const CXXConstructExpr *CE,
|
|
|
|
CheckerContext &C) const {
|
2011-08-28 06:51:26 +08:00
|
|
|
const CXXConstructorDecl *Ctor = CE->getConstructor();
|
|
|
|
if (!Ctor)
|
|
|
|
return;
|
|
|
|
|
2011-09-02 13:55:19 +08:00
|
|
|
RetainSummaryManager &Summaries = getSummaryManager(C);
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *Summ = Summaries.getSummary(Ctor);
|
2011-08-28 06:51:26 +08:00
|
|
|
|
|
|
|
// If we didn't get a summary, this constructor doesn't affect retain counts.
|
|
|
|
if (!Summ)
|
|
|
|
return;
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2012-01-07 06:09:28 +08:00
|
|
|
checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C);
|
2011-08-28 06:51:26 +08:00
|
|
|
}
|
|
|
|
|
2012-03-07 04:06:12 +08:00
|
|
|
void RetainCountChecker::processObjCLiterals(CheckerContext &C,
|
|
|
|
const Expr *Ex) const {
|
|
|
|
ProgramStateRef state = C.getState();
|
|
|
|
const ExplodedNode *pred = C.getPredecessor();
|
|
|
|
for (Stmt::const_child_iterator it = Ex->child_begin(), et = Ex->child_end() ;
|
|
|
|
it != et ; ++it) {
|
|
|
|
const Stmt *child = *it;
|
|
|
|
SVal V = state->getSVal(child, pred->getLocationContext());
|
|
|
|
if (SymbolRef sym = V.getAsSymbol())
|
|
|
|
if (const RefVal* T = state->get<RefBindings>(sym)) {
|
|
|
|
RefVal::Kind hasErr = (RefVal::Kind) 0;
|
|
|
|
state = updateSymbol(state, sym, *T, MayEscape, hasErr, C);
|
|
|
|
if (hasErr) {
|
|
|
|
processNonLeakError(state, child->getSourceRange(), hasErr, sym, C);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the object as autoreleased.
|
|
|
|
// RetEffect RE = RetEffect::MakeNotOwned(RetEffect::ObjC);
|
|
|
|
if (SymbolRef sym =
|
|
|
|
state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) {
|
|
|
|
QualType ResultTy = Ex->getType();
|
|
|
|
state = state->set<RefBindings>(sym, RefVal::makeNotOwned(RetEffect::ObjC,
|
|
|
|
ResultTy));
|
|
|
|
}
|
|
|
|
|
|
|
|
C.addTransition(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
// Apply the 'MayEscape' to all values.
|
|
|
|
processObjCLiterals(C, AL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
// Apply the 'MayEscape' to all keys and values.
|
|
|
|
processObjCLiterals(C, DL);
|
|
|
|
}
|
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg,
|
|
|
|
CheckerContext &C) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2011-08-23 07:48:23 +08:00
|
|
|
|
2011-09-02 13:55:19 +08:00
|
|
|
RetainSummaryManager &Summaries = getSummaryManager(C);
|
2011-08-25 08:10:37 +08:00
|
|
|
|
2011-10-06 07:54:29 +08:00
|
|
|
const RetainSummary *Summ;
|
2011-08-23 07:48:23 +08:00
|
|
|
if (Msg.isInstanceMessage()) {
|
2011-11-02 06:41:01 +08:00
|
|
|
const LocationContext *LC = C.getLocationContext();
|
2011-08-23 07:48:23 +08:00
|
|
|
Summ = Summaries.getInstanceMethodSummary(Msg, state, LC);
|
|
|
|
} else {
|
|
|
|
Summ = Summaries.getClassMethodSummary(Msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we didn't get a summary, this message doesn't affect retain counts.
|
|
|
|
if (!Summ)
|
|
|
|
return;
|
|
|
|
|
2012-01-07 06:09:28 +08:00
|
|
|
checkSummary(*Summ, CallOrObjCMessage(Msg, state, C.getLocationContext()), C);
|
2011-08-23 07:48:23 +08:00
|
|
|
}
|
|
|
|
|
2011-09-02 14:44:22 +08: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).
|
|
|
|
// FIXME: We may be able to do this with related result types instead.
|
|
|
|
// This function is probably overestimating.
|
|
|
|
static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) {
|
|
|
|
QualType RetTy = RetE->getType();
|
|
|
|
// If RetE is not a message expression just return its type.
|
|
|
|
// If RetE is a message expression, return its types if it is something
|
|
|
|
/// more specific than id.
|
|
|
|
if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(RetE))
|
|
|
|
if (const ObjCObjectPointerType *PT = RetTy->getAs<ObjCObjectPointerType>())
|
|
|
|
if (PT->isObjCQualifiedIdType() || PT->isObjCIdType() ||
|
|
|
|
PT->isObjCClassType()) {
|
|
|
|
// At this point we know the return type of the message expression is
|
|
|
|
// id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this
|
|
|
|
// is a call to a class method whose type we can resolve. In such
|
|
|
|
// cases, promote the return type to XXX* (where XXX is the class).
|
|
|
|
const ObjCInterfaceDecl *D = ME->getReceiverInterface();
|
|
|
|
return !D ? RetTy :
|
|
|
|
Ctx.getObjCObjectPointerType(Ctx.getObjCInterfaceType(D));
|
|
|
|
}
|
|
|
|
|
|
|
|
return RetTy;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RetainCountChecker::checkSummary(const RetainSummary &Summ,
|
|
|
|
const CallOrObjCMessage &CallOrMsg,
|
|
|
|
CheckerContext &C) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2011-08-23 07:48:23 +08:00
|
|
|
|
|
|
|
// Evaluate the effect of the arguments.
|
|
|
|
RefVal::Kind hasErr = (RefVal::Kind) 0;
|
|
|
|
SourceRange ErrorRange;
|
|
|
|
SymbolRef ErrorSym = 0;
|
|
|
|
|
|
|
|
for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) {
|
2011-08-28 06:51:26 +08:00
|
|
|
SVal V = CallOrMsg.getArgSVal(idx);
|
2011-08-23 07:48:23 +08:00
|
|
|
|
|
|
|
if (SymbolRef Sym = V.getAsLocSymbol()) {
|
|
|
|
if (RefBindings::data_type *T = state->get<RefBindings>(Sym)) {
|
2011-09-02 13:55:19 +08:00
|
|
|
state = updateSymbol(state, Sym, *T, Summ.getArg(idx), hasErr, C);
|
2011-08-23 07:48:23 +08:00
|
|
|
if (hasErr) {
|
|
|
|
ErrorRange = CallOrMsg.getArgSourceRange(idx);
|
|
|
|
ErrorSym = Sym;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Evaluate the effect on the message receiver.
|
|
|
|
bool ReceiverIsTracked = false;
|
2011-08-28 13:16:28 +08:00
|
|
|
if (!hasErr && CallOrMsg.isObjCMessage()) {
|
2011-10-27 05:06:44 +08:00
|
|
|
const LocationContext *LC = C.getLocationContext();
|
2011-08-28 13:16:28 +08:00
|
|
|
SVal Receiver = CallOrMsg.getInstanceMessageReceiver(LC);
|
|
|
|
if (SymbolRef Sym = Receiver.getAsLocSymbol()) {
|
2011-08-23 07:48:23 +08:00
|
|
|
if (const RefVal *T = state->get<RefBindings>(Sym)) {
|
|
|
|
ReceiverIsTracked = true;
|
2011-09-02 13:55:19 +08:00
|
|
|
state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(),
|
|
|
|
hasErr, C);
|
2011-08-23 07:48:23 +08:00
|
|
|
if (hasErr) {
|
2011-08-28 13:16:28 +08:00
|
|
|
ErrorRange = CallOrMsg.getReceiverSourceRange();
|
2011-08-23 07:48:23 +08:00
|
|
|
ErrorSym = Sym;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process any errors.
|
|
|
|
if (hasErr) {
|
|
|
|
processNonLeakError(state, ErrorRange, hasErr, ErrorSym, C);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Consult the summary for the return value.
|
|
|
|
RetEffect RE = Summ.getRetEffect();
|
|
|
|
|
|
|
|
if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) {
|
2011-08-25 08:10:37 +08:00
|
|
|
if (ReceiverIsTracked)
|
2011-09-02 13:55:19 +08:00
|
|
|
RE = getSummaryManager(C).getObjAllocRetEffect();
|
2011-08-25 08:10:37 +08:00
|
|
|
else
|
2011-08-23 07:48:23 +08:00
|
|
|
RE = RetEffect::MakeNoRet();
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (RE.getKind()) {
|
|
|
|
default:
|
2012-01-17 14:56:22 +08:00
|
|
|
llvm_unreachable("Unhandled RetEffect.");
|
2011-08-23 07:48:23 +08:00
|
|
|
|
|
|
|
case RetEffect::NoRet:
|
|
|
|
// No work necessary.
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RetEffect::OwnedAllocatedSymbol:
|
|
|
|
case RetEffect::OwnedSymbol: {
|
2012-01-07 06:09:28 +08:00
|
|
|
SymbolRef Sym = state->getSVal(CallOrMsg.getOriginExpr(),
|
|
|
|
C.getLocationContext()).getAsSymbol();
|
2011-08-23 07:48:23 +08:00
|
|
|
if (!Sym)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Use the result type from callOrMsg as it automatically adjusts
|
|
|
|
// for methods/functions that return references.
|
|
|
|
QualType ResultTy = CallOrMsg.getResultType(C.getASTContext());
|
|
|
|
state = state->set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(),
|
|
|
|
ResultTy));
|
|
|
|
|
|
|
|
// FIXME: Add a flag to the checker where allocations are assumed to
|
|
|
|
// *not* fail. (The code below is out-of-date, though.)
|
|
|
|
#if 0
|
|
|
|
if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) {
|
|
|
|
bool isFeasible;
|
|
|
|
state = state.assume(loc::SymbolVal(Sym), true, isFeasible);
|
|
|
|
assert(isFeasible && "Cannot assume fresh symbol is non-null.");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RetEffect::GCNotOwnedSymbol:
|
|
|
|
case RetEffect::ARCNotOwnedSymbol:
|
|
|
|
case RetEffect::NotOwnedSymbol: {
|
|
|
|
const Expr *Ex = CallOrMsg.getOriginExpr();
|
2012-01-07 06:09:28 +08:00
|
|
|
SymbolRef Sym = state->getSVal(Ex, C.getLocationContext()).getAsSymbol();
|
2011-08-23 07:48:23 +08:00
|
|
|
if (!Sym)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Use GetReturnType in order to give [NSFoo alloc] the type NSFoo *.
|
|
|
|
QualType ResultTy = GetReturnType(Ex, C.getASTContext());
|
|
|
|
state = state->set<RefBindings>(Sym, RefVal::makeNotOwned(RE.getObjKind(),
|
|
|
|
ResultTy));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This check is actually necessary; otherwise the statement builder thinks
|
|
|
|
// we've hit a previously-found path.
|
|
|
|
// Normally addTransition takes care of this, but we want the node pointer.
|
|
|
|
ExplodedNode *NewNode;
|
|
|
|
if (state == C.getState()) {
|
|
|
|
NewNode = C.getPredecessor();
|
|
|
|
} else {
|
2011-10-27 05:06:34 +08:00
|
|
|
NewNode = C.addTransition(state);
|
2011-08-23 07:48:23 +08:00
|
|
|
}
|
|
|
|
|
2011-08-25 02:56:32 +08:00
|
|
|
// Annotate the node with summary we used.
|
|
|
|
if (NewNode) {
|
|
|
|
// FIXME: This is ugly. See checkEndAnalysis for why it's necessary.
|
|
|
|
if (ShouldResetSummaryLog) {
|
|
|
|
SummaryLog.clear();
|
|
|
|
ShouldResetSummaryLog = false;
|
|
|
|
}
|
2011-08-24 04:55:48 +08:00
|
|
|
SummaryLog[NewNode] = &Summ;
|
2011-08-25 02:56:32 +08:00
|
|
|
}
|
2011-08-23 07:48:23 +08:00
|
|
|
}
|
|
|
|
|
2011-08-24 04:27:16 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef
|
|
|
|
RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
|
2011-09-02 14:44:22 +08:00
|
|
|
RefVal V, ArgEffect E, RefVal::Kind &hasErr,
|
|
|
|
CheckerContext &C) const {
|
2011-08-24 04:27:16 +08:00
|
|
|
// In GC mode [... release] and [... retain] do nothing.
|
2011-09-02 14:44:22 +08:00
|
|
|
// In ARC mode they shouldn't exist at all, but we just ignore them.
|
2011-09-02 13:55:19 +08:00
|
|
|
bool IgnoreRetainMsg = C.isObjCGCEnabled();
|
|
|
|
if (!IgnoreRetainMsg)
|
2012-03-11 15:00:24 +08:00
|
|
|
IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount;
|
2011-09-02 13:55:19 +08:00
|
|
|
|
2011-08-24 04:27:16 +08:00
|
|
|
switch (E) {
|
|
|
|
default: break;
|
2011-09-02 13:55:19 +08:00
|
|
|
case IncRefMsg: E = IgnoreRetainMsg ? DoNothing : IncRef; break;
|
|
|
|
case DecRefMsg: E = IgnoreRetainMsg ? DoNothing : DecRef; break;
|
|
|
|
case MakeCollectable: E = C.isObjCGCEnabled() ? DecRef : DoNothing; break;
|
|
|
|
case NewAutoreleasePool: E = C.isObjCGCEnabled() ? DoNothing :
|
|
|
|
NewAutoreleasePool; break;
|
2011-08-24 04:27:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Handle all use-after-releases.
|
2011-09-02 13:55:19 +08:00
|
|
|
if (!C.isObjCGCEnabled() && V.getKind() == RefVal::Released) {
|
2011-08-24 04:27:16 +08:00
|
|
|
V = V ^ RefVal::ErrorUseAfterRelease;
|
|
|
|
hasErr = V.getKind();
|
|
|
|
return state->set<RefBindings>(sym, V);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (E) {
|
|
|
|
case DecRefMsg:
|
|
|
|
case IncRefMsg:
|
|
|
|
case MakeCollectable:
|
|
|
|
llvm_unreachable("DecRefMsg/IncRefMsg/MakeCollectable already converted");
|
|
|
|
|
|
|
|
case Dealloc:
|
|
|
|
// Any use of -dealloc in GC is *bad*.
|
2011-09-02 13:55:19 +08:00
|
|
|
if (C.isObjCGCEnabled()) {
|
2011-08-24 04:27:16 +08:00
|
|
|
V = V ^ RefVal::ErrorDeallocGC;
|
|
|
|
hasErr = V.getKind();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (V.getKind()) {
|
|
|
|
default:
|
|
|
|
llvm_unreachable("Invalid RefVal state for an explicit dealloc.");
|
|
|
|
case RefVal::Owned:
|
|
|
|
// The object immediately transitions to the released state.
|
|
|
|
V = V ^ RefVal::Released;
|
|
|
|
V.clearCounts();
|
|
|
|
return state->set<RefBindings>(sym, V);
|
|
|
|
case RefVal::NotOwned:
|
|
|
|
V = V ^ RefVal::ErrorDeallocNotOwned;
|
|
|
|
hasErr = V.getKind();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NewAutoreleasePool:
|
2011-09-02 13:55:19 +08:00
|
|
|
assert(!C.isObjCGCEnabled());
|
2011-08-24 04:27:16 +08:00
|
|
|
return state->add<AutoreleaseStack>(sym);
|
|
|
|
|
|
|
|
case MayEscape:
|
|
|
|
if (V.getKind() == RefVal::Owned) {
|
|
|
|
V = V ^ RefVal::NotOwned;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fall-through.
|
|
|
|
|
|
|
|
case DoNothing:
|
|
|
|
return state;
|
|
|
|
|
|
|
|
case Autorelease:
|
2011-09-02 13:55:19 +08:00
|
|
|
if (C.isObjCGCEnabled())
|
2011-08-24 04:27:16 +08:00
|
|
|
return state;
|
|
|
|
|
|
|
|
// Update the autorelease counts.
|
|
|
|
state = SendAutorelease(state, ARCountFactory, sym);
|
|
|
|
V = V.autorelease();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case StopTracking:
|
|
|
|
return state->remove<RefBindings>(sym);
|
|
|
|
|
|
|
|
case IncRef:
|
|
|
|
switch (V.getKind()) {
|
|
|
|
default:
|
|
|
|
llvm_unreachable("Invalid RefVal state for a retain.");
|
|
|
|
case RefVal::Owned:
|
|
|
|
case RefVal::NotOwned:
|
|
|
|
V = V + 1;
|
|
|
|
break;
|
|
|
|
case RefVal::Released:
|
|
|
|
// Non-GC cases are handled above.
|
2011-09-02 13:55:19 +08:00
|
|
|
assert(C.isObjCGCEnabled());
|
2011-08-24 04:27:16 +08:00
|
|
|
V = (V ^ RefVal::Owned) + 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SelfOwn:
|
|
|
|
V = V ^ RefVal::NotOwned;
|
|
|
|
// Fall-through.
|
|
|
|
case DecRef:
|
|
|
|
case DecRefBridgedTransfered:
|
|
|
|
switch (V.getKind()) {
|
|
|
|
default:
|
|
|
|
// case 'RefVal::Released' handled above.
|
|
|
|
llvm_unreachable("Invalid RefVal state for a release.");
|
|
|
|
|
|
|
|
case RefVal::Owned:
|
|
|
|
assert(V.getCount() > 0);
|
|
|
|
if (V.getCount() == 1)
|
|
|
|
V = V ^ (E == DecRefBridgedTransfered ?
|
|
|
|
RefVal::NotOwned : RefVal::Released);
|
|
|
|
V = V - 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::NotOwned:
|
|
|
|
if (V.getCount() > 0)
|
|
|
|
V = V - 1;
|
|
|
|
else {
|
|
|
|
V = V ^ RefVal::ErrorReleaseNotOwned;
|
|
|
|
hasErr = V.getKind();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::Released:
|
|
|
|
// Non-GC cases are handled above.
|
2011-09-02 13:55:19 +08:00
|
|
|
assert(C.isObjCGCEnabled());
|
2011-08-24 04:27:16 +08:00
|
|
|
V = V ^ RefVal::ErrorUseAfterRelease;
|
|
|
|
hasErr = V.getKind();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return state->set<RefBindings>(sym, V);
|
|
|
|
}
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
void RetainCountChecker::processNonLeakError(ProgramStateRef St,
|
2011-09-02 14:44:22 +08:00
|
|
|
SourceRange ErrorRange,
|
|
|
|
RefVal::Kind ErrorKind,
|
|
|
|
SymbolRef Sym,
|
|
|
|
CheckerContext &C) const {
|
2011-08-23 07:48:23 +08:00
|
|
|
ExplodedNode *N = C.generateSink(St);
|
|
|
|
if (!N)
|
|
|
|
return;
|
|
|
|
|
|
|
|
CFRefBug *BT;
|
|
|
|
switch (ErrorKind) {
|
|
|
|
default:
|
|
|
|
llvm_unreachable("Unhandled error.");
|
|
|
|
case RefVal::ErrorUseAfterRelease:
|
2011-08-25 08:34:03 +08:00
|
|
|
if (!useAfterRelease)
|
|
|
|
useAfterRelease.reset(new UseAfterRelease());
|
|
|
|
BT = &*useAfterRelease;
|
2011-08-23 07:48:23 +08:00
|
|
|
break;
|
|
|
|
case RefVal::ErrorReleaseNotOwned:
|
2011-08-25 08:34:03 +08:00
|
|
|
if (!releaseNotOwned)
|
|
|
|
releaseNotOwned.reset(new BadRelease());
|
|
|
|
BT = &*releaseNotOwned;
|
2011-08-23 07:48:23 +08:00
|
|
|
break;
|
|
|
|
case RefVal::ErrorDeallocGC:
|
2011-08-25 08:34:03 +08:00
|
|
|
if (!deallocGC)
|
|
|
|
deallocGC.reset(new DeallocGC());
|
|
|
|
BT = &*deallocGC;
|
2011-08-23 07:48:23 +08:00
|
|
|
break;
|
|
|
|
case RefVal::ErrorDeallocNotOwned:
|
2011-08-25 08:34:03 +08:00
|
|
|
if (!deallocNotOwned)
|
|
|
|
deallocNotOwned.reset(new DeallocNotOwned());
|
|
|
|
BT = &*deallocNotOwned;
|
2011-08-23 07:48:23 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-08-25 08:34:03 +08:00
|
|
|
assert(BT);
|
2012-03-11 15:00:24 +08:00
|
|
|
CFRefReport *report = new CFRefReport(*BT, C.getASTContext().getLangOpts(),
|
2011-09-02 13:55:19 +08:00
|
|
|
C.isObjCGCEnabled(), SummaryLog,
|
|
|
|
N, Sym);
|
2011-08-23 07:48:23 +08:00
|
|
|
report->addRange(ErrorRange);
|
|
|
|
C.EmitReport(report);
|
|
|
|
}
|
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Handle the return values of retain-count-related functions.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
|
2011-08-22 05:58:18 +08:00
|
|
|
// Get the callee. We're only interested in simple C functions.
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2011-12-01 13:57:37 +08:00
|
|
|
const FunctionDecl *FD = C.getCalleeDecl(CE);
|
2011-08-22 05:58:18 +08:00
|
|
|
if (!FD)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
IdentifierInfo *II = FD->getIdentifier();
|
|
|
|
if (!II)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// For now, we're only handling the functions that return aliases of their
|
|
|
|
// arguments: CFRetain and CFMakeCollectable (and their families).
|
|
|
|
// Eventually we should add other functions we can model entirely,
|
|
|
|
// such as CFRelease, which don't invalidate their arguments or globals.
|
|
|
|
if (CE->getNumArgs() != 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Get the name of the function.
|
|
|
|
StringRef FName = II->getName();
|
|
|
|
FName = FName.substr(FName.find_first_not_of('_'));
|
|
|
|
|
|
|
|
// See if it's one of the specific functions we know how to eval.
|
|
|
|
bool canEval = false;
|
|
|
|
|
2011-12-01 13:57:37 +08:00
|
|
|
QualType ResultTy = CE->getCallReturnType();
|
2011-08-22 05:58:18 +08:00
|
|
|
if (ResultTy->isObjCIdType()) {
|
|
|
|
// Handle: id NSMakeCollectable(CFTypeRef)
|
|
|
|
canEval = II->isStr("NSMakeCollectable");
|
|
|
|
} else if (ResultTy->isPointerType()) {
|
|
|
|
// Handle: (CF|CG)Retain
|
|
|
|
// CFMakeCollectable
|
|
|
|
// It's okay to be a little sloppy here (CGMakeCollectable doesn't exist).
|
|
|
|
if (cocoa::isRefType(ResultTy, "CF", FName) ||
|
|
|
|
cocoa::isRefType(ResultTy, "CG", FName)) {
|
|
|
|
canEval = isRetain(FD, FName) || isMakeCollectable(FD, FName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!canEval)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Bind the return value.
|
2012-01-07 06:09:28 +08:00
|
|
|
const LocationContext *LCtx = C.getLocationContext();
|
|
|
|
SVal RetVal = state->getSVal(CE->getArg(0), LCtx);
|
2011-08-22 05:58:18 +08:00
|
|
|
if (RetVal.isUnknown()) {
|
|
|
|
// If the receiver is unknown, conjure a return value.
|
|
|
|
SValBuilder &SVB = C.getSValBuilder();
|
2011-10-05 04:43:05 +08:00
|
|
|
unsigned Count = C.getCurrentBlockCount();
|
2012-02-18 07:13:45 +08:00
|
|
|
SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count);
|
2011-08-22 05:58:18 +08:00
|
|
|
}
|
2012-01-07 06:09:28 +08:00
|
|
|
state = state->BindExpr(CE, LCtx, RetVal, false);
|
2011-08-22 05:58:18 +08:00
|
|
|
|
2011-08-23 07:48:23 +08:00
|
|
|
// FIXME: This should not be necessary, but otherwise the argument seems to be
|
|
|
|
// considered alive during the next statement.
|
|
|
|
if (const MemRegion *ArgRegion = RetVal.getAsRegion()) {
|
|
|
|
// Save the refcount status of the argument.
|
|
|
|
SymbolRef Sym = RetVal.getAsLocSymbol();
|
|
|
|
RefBindings::data_type *Binding = 0;
|
|
|
|
if (Sym)
|
|
|
|
Binding = state->get<RefBindings>(Sym);
|
2011-08-22 05:58:18 +08:00
|
|
|
|
2011-08-23 07:48:23 +08:00
|
|
|
// Invalidate the argument region.
|
2011-10-05 04:43:05 +08:00
|
|
|
unsigned Count = C.getCurrentBlockCount();
|
2012-02-18 07:13:45 +08:00
|
|
|
state = state->invalidateRegions(ArgRegion, CE, Count, LCtx);
|
2011-08-22 05:58:18 +08:00
|
|
|
|
2011-08-23 07:48:23 +08:00
|
|
|
// Restore the refcount status of the argument.
|
|
|
|
if (Binding)
|
|
|
|
state = state->set<RefBindings>(Sym, *Binding);
|
|
|
|
}
|
|
|
|
|
2011-10-27 05:06:34 +08:00
|
|
|
C.addTransition(state);
|
2011-08-22 05:58:18 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Handle return statements.
|
|
|
|
//===----------------------------------------------------------------------===//
|
2011-08-24 03:43:16 +08:00
|
|
|
|
2012-02-25 10:09:09 +08:00
|
|
|
// Return true if the current LocationContext has no caller context.
|
|
|
|
static bool inTopFrame(CheckerContext &C) {
|
|
|
|
const LocationContext *LC = C.getLocationContext();
|
|
|
|
return LC->getParent() == 0;
|
|
|
|
}
|
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
void RetainCountChecker::checkPreStmt(const ReturnStmt *S,
|
|
|
|
CheckerContext &C) const {
|
2012-02-25 10:09:09 +08:00
|
|
|
|
|
|
|
// Only adjust the reference count if this is the top-level call frame,
|
|
|
|
// and not the result of inlining. In the future, we should do
|
|
|
|
// better checking even for inlined calls, and see if they match
|
|
|
|
// with their expected semantics (e.g., the method should return a retained
|
|
|
|
// object, etc.).
|
|
|
|
if (!inTopFrame(C))
|
|
|
|
return;
|
|
|
|
|
2011-08-24 03:43:16 +08:00
|
|
|
const Expr *RetE = S->getRetValue();
|
|
|
|
if (!RetE)
|
|
|
|
return;
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2012-01-07 06:09:28 +08:00
|
|
|
SymbolRef Sym =
|
|
|
|
state->getSValAsScalarOrLoc(RetE, C.getLocationContext()).getAsLocSymbol();
|
2011-08-24 03:43:16 +08:00
|
|
|
if (!Sym)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Get the reference count binding (if any).
|
|
|
|
const RefVal *T = state->get<RefBindings>(Sym);
|
|
|
|
if (!T)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Change the reference count.
|
|
|
|
RefVal X = *T;
|
|
|
|
|
|
|
|
switch (X.getKind()) {
|
|
|
|
case RefVal::Owned: {
|
|
|
|
unsigned cnt = X.getCount();
|
|
|
|
assert(cnt > 0);
|
|
|
|
X.setCount(cnt - 1);
|
|
|
|
X = X ^ RefVal::ReturnedOwned;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RefVal::NotOwned: {
|
|
|
|
unsigned cnt = X.getCount();
|
|
|
|
if (cnt) {
|
|
|
|
X.setCount(cnt - 1);
|
|
|
|
X = X ^ RefVal::ReturnedOwned;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
X = X ^ RefVal::ReturnedNotOwned;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the binding.
|
|
|
|
state = state->set<RefBindings>(Sym, X);
|
2011-10-27 05:06:34 +08:00
|
|
|
ExplodedNode *Pred = C.addTransition(state);
|
2011-08-24 03:43:16 +08:00
|
|
|
|
|
|
|
// At this point we have updated the state properly.
|
|
|
|
// Everything after this is merely checking to see if the return value has
|
|
|
|
// been over- or under-retained.
|
|
|
|
|
|
|
|
// Did we cache out?
|
|
|
|
if (!Pred)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Update the autorelease counts.
|
|
|
|
static SimpleProgramPointTag
|
2011-09-02 14:44:22 +08:00
|
|
|
AutoreleaseTag("RetainCountChecker : Autorelease");
|
2011-10-06 07:44:11 +08:00
|
|
|
GenericNodeBuilderRefCount Bd(C, &AutoreleaseTag);
|
2011-10-26 03:57:11 +08:00
|
|
|
llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, C, Sym, X);
|
2011-08-24 03:43:16 +08:00
|
|
|
|
|
|
|
// Did we cache out?
|
2011-08-24 04:07:14 +08:00
|
|
|
if (!Pred)
|
2011-08-24 03:43:16 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Get the updated binding.
|
|
|
|
T = state->get<RefBindings>(Sym);
|
|
|
|
assert(T);
|
|
|
|
X = *T;
|
|
|
|
|
|
|
|
// Consult the summary of the enclosing method.
|
2011-09-02 13:55:19 +08:00
|
|
|
RetainSummaryManager &Summaries = getSummaryManager(C);
|
2011-08-24 03:43:16 +08:00
|
|
|
const Decl *CD = &Pred->getCodeDecl();
|
|
|
|
|
|
|
|
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) {
|
|
|
|
// Unlike regular functions, /all/ ObjC methods are assumed to always
|
|
|
|
// follow Cocoa retain-count conventions, not just those with special
|
|
|
|
// names or attributes.
|
2011-08-25 08:10:37 +08:00
|
|
|
const RetainSummary *Summ = Summaries.getMethodSummary(MD);
|
2011-08-24 03:43:16 +08:00
|
|
|
RetEffect RE = Summ ? Summ->getRetEffect() : RetEffect::MakeNoRet();
|
|
|
|
checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) {
|
|
|
|
if (!isa<CXXMethodDecl>(FD))
|
2011-08-25 08:10:37 +08:00
|
|
|
if (const RetainSummary *Summ = Summaries.getSummary(FD))
|
2011-08-24 03:43:16 +08:00
|
|
|
checkReturnWithRetEffect(S, C, Pred, Summ->getRetEffect(), X,
|
|
|
|
Sym, state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
|
|
|
|
CheckerContext &C,
|
|
|
|
ExplodedNode *Pred,
|
|
|
|
RetEffect RE, RefVal X,
|
|
|
|
SymbolRef Sym,
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state) const {
|
2011-08-24 03:43:16 +08:00
|
|
|
// Any leaks or other errors?
|
|
|
|
if (X.isReturnedOwned() && X.getCount() == 0) {
|
|
|
|
if (RE.getKind() != RetEffect::NoRet) {
|
|
|
|
bool hasError = false;
|
2011-09-02 13:55:19 +08:00
|
|
|
if (C.isObjCGCEnabled() && RE.getObjKind() == RetEffect::ObjC) {
|
2011-08-24 03:43:16 +08:00
|
|
|
// Things are more complicated with garbage collection. If the
|
|
|
|
// returned object is suppose to be an Objective-C object, we have
|
|
|
|
// a leak (as the caller expects a GC'ed object) because no
|
|
|
|
// method should return ownership unless it returns a CF object.
|
|
|
|
hasError = true;
|
|
|
|
X = X ^ RefVal::ErrorGCLeakReturned;
|
|
|
|
}
|
|
|
|
else if (!RE.isOwned()) {
|
|
|
|
// Either we are using GC and the returned object is a CF type
|
|
|
|
// or we aren't using GC. In either case, we expect that the
|
|
|
|
// enclosing method is expected to return ownership.
|
|
|
|
hasError = true;
|
|
|
|
X = X ^ RefVal::ErrorLeakReturned;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hasError) {
|
|
|
|
// Generate an error node.
|
|
|
|
state = state->set<RefBindings>(Sym, X);
|
|
|
|
|
|
|
|
static SimpleProgramPointTag
|
2011-09-02 14:44:22 +08:00
|
|
|
ReturnOwnLeakTag("RetainCountChecker : ReturnsOwnLeak");
|
2011-10-27 05:06:34 +08:00
|
|
|
ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag);
|
2011-08-24 03:43:16 +08:00
|
|
|
if (N) {
|
2012-03-11 15:00:24 +08:00
|
|
|
const LangOptions &LOpts = C.getASTContext().getLangOpts();
|
2011-09-02 13:55:19 +08:00
|
|
|
bool GCEnabled = C.isObjCGCEnabled();
|
2011-08-24 03:43:16 +08:00
|
|
|
CFRefReport *report =
|
2011-09-02 13:55:19 +08:00
|
|
|
new CFRefLeakReport(*getLeakAtReturnBug(LOpts, GCEnabled),
|
|
|
|
LOpts, GCEnabled, SummaryLog,
|
2011-10-26 03:57:11 +08:00
|
|
|
N, Sym, C);
|
2011-08-24 03:43:16 +08:00
|
|
|
C.EmitReport(report);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (X.isReturnedNotOwned()) {
|
|
|
|
if (RE.isOwned()) {
|
|
|
|
// Trying to return a not owned object to a caller expecting an
|
|
|
|
// owned object.
|
|
|
|
state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned);
|
|
|
|
|
|
|
|
static SimpleProgramPointTag
|
2011-09-02 14:44:22 +08:00
|
|
|
ReturnNotOwnedTag("RetainCountChecker : ReturnNotOwnedForOwned");
|
2011-10-27 05:06:34 +08:00
|
|
|
ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag);
|
2011-08-24 03:43:16 +08:00
|
|
|
if (N) {
|
2011-08-25 08:34:03 +08:00
|
|
|
if (!returnNotOwnedForOwned)
|
|
|
|
returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned());
|
|
|
|
|
2011-08-24 03:43:16 +08:00
|
|
|
CFRefReport *report =
|
2011-08-25 08:34:03 +08:00
|
|
|
new CFRefReport(*returnNotOwnedForOwned,
|
2012-03-11 15:00:24 +08:00
|
|
|
C.getASTContext().getLangOpts(),
|
2011-09-02 13:55:19 +08:00
|
|
|
C.isObjCGCEnabled(), SummaryLog, N, Sym);
|
2011-08-24 03:43:16 +08:00
|
|
|
C.EmitReport(report);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Check various ways a symbol can be invalidated.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2011-10-06 08:43:15 +08:00
|
|
|
void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S,
|
2011-09-02 14:44:22 +08:00
|
|
|
CheckerContext &C) const {
|
|
|
|
// Are we storing to something that causes the value to "escape"?
|
|
|
|
bool escapes = true;
|
|
|
|
|
|
|
|
// 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
|
|
|
|
// does not understand.
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2011-09-02 14:44:22 +08:00
|
|
|
|
|
|
|
if (loc::MemRegionVal *regionLoc = dyn_cast<loc::MemRegionVal>(&loc)) {
|
|
|
|
escapes = !regionLoc->getRegion()->hasStackStorage();
|
|
|
|
|
|
|
|
if (!escapes) {
|
|
|
|
// To test (3), generate a new state with the binding added. If it is
|
|
|
|
// the same state, then it escapes (since the store cannot represent
|
|
|
|
// the binding).
|
|
|
|
escapes = (state == (state->bindLoc(*regionLoc, val)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
// Otherwise, find all symbols referenced by 'val' that we are tracking
|
|
|
|
// and stop tracking them.
|
|
|
|
state = state->scanReachableSymbols<StopTrackingCallback>(val).getState();
|
2011-10-27 05:06:34 +08:00
|
|
|
C.addTransition(state);
|
2011-09-02 14:44:22 +08:00
|
|
|
}
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state,
|
2011-09-02 14:44:22 +08:00
|
|
|
SVal Cond,
|
|
|
|
bool Assumption) const {
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
RefBindings B = state->get<RefBindings>();
|
|
|
|
|
|
|
|
if (B.isEmpty())
|
|
|
|
return state;
|
|
|
|
|
|
|
|
bool changed = false;
|
|
|
|
RefBindings::Factory &RefBFactory = state->get_context<RefBindings>();
|
|
|
|
|
|
|
|
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.
|
|
|
|
if (state->getSymVal(I.getKey())) {
|
|
|
|
changed = true;
|
|
|
|
B = RefBFactory.remove(B, I.getKey());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (changed)
|
|
|
|
state = state->set<RefBindings>(B);
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef
|
|
|
|
RetainCountChecker::checkRegionChanges(ProgramStateRef state,
|
2011-09-02 14:44:22 +08:00
|
|
|
const StoreManager::InvalidatedSymbols *invalidated,
|
|
|
|
ArrayRef<const MemRegion *> ExplicitRegions,
|
2012-02-15 05:55:24 +08:00
|
|
|
ArrayRef<const MemRegion *> Regions,
|
|
|
|
const CallOrObjCMessage *Call) const {
|
2011-09-02 14:44:22 +08:00
|
|
|
if (!invalidated)
|
|
|
|
return state;
|
|
|
|
|
|
|
|
llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols;
|
|
|
|
for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(),
|
|
|
|
E = ExplicitRegions.end(); I != E; ++I) {
|
|
|
|
if (const SymbolicRegion *SR = (*I)->StripCasts()->getAs<SymbolicRegion>())
|
|
|
|
WhitelistedSymbols.insert(SR->getSymbol());
|
|
|
|
}
|
|
|
|
|
|
|
|
for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(),
|
|
|
|
E = invalidated->end(); I!=E; ++I) {
|
|
|
|
SymbolRef sym = *I;
|
|
|
|
if (WhitelistedSymbols.count(sym))
|
|
|
|
continue;
|
|
|
|
// Remove any existing reference-count binding.
|
|
|
|
state = state->remove<RefBindings>(sym);
|
|
|
|
}
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
2011-08-24 04:07:14 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Handle dead symbols and end-of-path.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
std::pair<ExplodedNode *, ProgramStateRef >
|
|
|
|
RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
|
2011-09-02 14:44:22 +08:00
|
|
|
GenericNodeBuilderRefCount Bd,
|
2011-10-26 03:57:11 +08:00
|
|
|
ExplodedNode *Pred,
|
|
|
|
CheckerContext &Ctx,
|
2011-09-02 14:44:22 +08:00
|
|
|
SymbolRef Sym, RefVal V) const {
|
2011-08-24 04:07:14 +08:00
|
|
|
unsigned ACnt = V.getAutoreleaseCount();
|
|
|
|
|
|
|
|
// No autorelease counts? Nothing to be done.
|
|
|
|
if (!ACnt)
|
|
|
|
return std::make_pair(Pred, state);
|
|
|
|
|
2011-10-26 03:57:11 +08:00
|
|
|
assert(!Ctx.isObjCGCEnabled() && "Autorelease counts in GC mode?");
|
2011-08-24 04:07:14 +08:00
|
|
|
unsigned Cnt = V.getCount();
|
|
|
|
|
|
|
|
// FIXME: Handle sending 'autorelease' to already released object.
|
|
|
|
|
|
|
|
if (V.getKind() == RefVal::ReturnedOwned)
|
|
|
|
++Cnt;
|
|
|
|
|
|
|
|
if (ACnt <= Cnt) {
|
|
|
|
if (ACnt == Cnt) {
|
|
|
|
V.clearCounts();
|
|
|
|
if (V.getKind() == RefVal::ReturnedOwned)
|
|
|
|
V = V ^ RefVal::ReturnedNotOwned;
|
|
|
|
else
|
|
|
|
V = V ^ RefVal::NotOwned;
|
|
|
|
} else {
|
|
|
|
V.setCount(Cnt - ACnt);
|
|
|
|
V.setAutoreleaseCount(0);
|
|
|
|
}
|
|
|
|
state = state->set<RefBindings>(Sym, V);
|
|
|
|
ExplodedNode *N = Bd.MakeNode(state, Pred);
|
|
|
|
if (N == 0)
|
|
|
|
state = 0;
|
|
|
|
return std::make_pair(N, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Woah! More autorelease counts then retain counts left.
|
|
|
|
// Emit hard error.
|
|
|
|
V = V ^ RefVal::ErrorOverAutorelease;
|
|
|
|
state = state->set<RefBindings>(Sym, V);
|
|
|
|
|
2011-10-19 07:05:58 +08:00
|
|
|
if (ExplodedNode *N = Bd.MakeNode(state, Pred, true)) {
|
2012-02-05 10:13:05 +08:00
|
|
|
SmallString<128> sbuf;
|
2011-08-24 04:07:14 +08:00
|
|
|
llvm::raw_svector_ostream os(sbuf);
|
|
|
|
os << "Object over-autoreleased: object was sent -autorelease ";
|
|
|
|
if (V.getAutoreleaseCount() > 1)
|
|
|
|
os << V.getAutoreleaseCount() << " times ";
|
|
|
|
os << "but the object has a +" << V.getCount() << " retain count";
|
|
|
|
|
2011-08-25 08:34:03 +08:00
|
|
|
if (!overAutorelease)
|
|
|
|
overAutorelease.reset(new OverAutorelease());
|
|
|
|
|
2012-03-11 15:00:24 +08:00
|
|
|
const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
|
2011-08-24 04:07:14 +08:00
|
|
|
CFRefReport *report =
|
2011-08-25 08:34:03 +08:00
|
|
|
new CFRefReport(*overAutorelease, LOpts, /* GCEnabled = */ false,
|
|
|
|
SummaryLog, N, Sym, os.str());
|
2011-10-26 03:57:11 +08:00
|
|
|
Ctx.EmitReport(report);
|
2011-08-24 04:07:14 +08:00
|
|
|
}
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
return std::make_pair((ExplodedNode *)0, (ProgramStateRef )0);
|
2011-08-24 04:07:14 +08:00
|
|
|
}
|
2011-08-24 03:01:07 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef
|
|
|
|
RetainCountChecker::handleSymbolDeath(ProgramStateRef state,
|
2011-09-02 14:44:22 +08:00
|
|
|
SymbolRef sid, RefVal V,
|
2011-08-24 03:01:07 +08:00
|
|
|
SmallVectorImpl<SymbolRef> &Leaked) const {
|
2011-08-24 12:48:19 +08:00
|
|
|
bool hasLeak = false;
|
2011-08-24 03:01:07 +08:00
|
|
|
if (V.isOwned())
|
|
|
|
hasLeak = true;
|
|
|
|
else if (V.isNotOwned() || V.isReturnedOwned())
|
|
|
|
hasLeak = (V.getCount() > 0);
|
|
|
|
|
|
|
|
if (!hasLeak)
|
|
|
|
return state->remove<RefBindings>(sid);
|
|
|
|
|
|
|
|
Leaked.push_back(sid);
|
|
|
|
return state->set<RefBindings>(sid, V ^ RefVal::ErrorLeak);
|
|
|
|
}
|
|
|
|
|
|
|
|
ExplodedNode *
|
2012-01-27 05:29:00 +08:00
|
|
|
RetainCountChecker::processLeaks(ProgramStateRef state,
|
2011-09-02 14:44:22 +08:00
|
|
|
SmallVectorImpl<SymbolRef> &Leaked,
|
|
|
|
GenericNodeBuilderRefCount &Builder,
|
2011-10-26 03:57:11 +08:00
|
|
|
CheckerContext &Ctx,
|
|
|
|
ExplodedNode *Pred) const {
|
2011-08-24 03:01:07 +08:00
|
|
|
if (Leaked.empty())
|
|
|
|
return Pred;
|
|
|
|
|
|
|
|
// Generate an intermediate node representing the leak point.
|
|
|
|
ExplodedNode *N = Builder.MakeNode(state, Pred);
|
|
|
|
|
|
|
|
if (N) {
|
|
|
|
for (SmallVectorImpl<SymbolRef>::iterator
|
|
|
|
I = Leaked.begin(), E = Leaked.end(); I != E; ++I) {
|
|
|
|
|
2012-03-11 15:00:24 +08:00
|
|
|
const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
|
2011-10-26 03:57:11 +08:00
|
|
|
bool GCEnabled = Ctx.isObjCGCEnabled();
|
2011-09-02 13:55:19 +08:00
|
|
|
CFRefBug *BT = Pred ? getLeakWithinFunctionBug(LOpts, GCEnabled)
|
|
|
|
: getLeakAtReturnBug(LOpts, GCEnabled);
|
2011-08-24 03:01:07 +08:00
|
|
|
assert(BT && "BugType not initialized.");
|
2011-08-25 06:39:09 +08:00
|
|
|
|
2011-09-02 13:55:19 +08:00
|
|
|
CFRefLeakReport *report = new CFRefLeakReport(*BT, LOpts, GCEnabled,
|
2011-10-26 03:57:11 +08:00
|
|
|
SummaryLog, N, *I, Ctx);
|
|
|
|
Ctx.EmitReport(report);
|
2011-08-24 03:01:07 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return N;
|
|
|
|
}
|
|
|
|
|
2011-10-26 03:56:48 +08:00
|
|
|
void RetainCountChecker::checkEndPath(CheckerContext &Ctx) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = Ctx.getState();
|
2011-10-26 03:56:48 +08:00
|
|
|
GenericNodeBuilderRefCount Bd(Ctx);
|
2011-08-24 03:01:07 +08:00
|
|
|
RefBindings B = state->get<RefBindings>();
|
2011-10-26 03:56:48 +08:00
|
|
|
ExplodedNode *Pred = Ctx.getPredecessor();
|
2011-08-24 03:01:07 +08:00
|
|
|
|
|
|
|
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
|
2011-10-26 03:57:11 +08:00
|
|
|
llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, Ctx,
|
2011-08-24 04:07:14 +08:00
|
|
|
I->first, I->second);
|
|
|
|
if (!state)
|
2011-08-24 03:01:07 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-02-07 08:24:33 +08:00
|
|
|
// If the current LocationContext has a parent, don't check for leaks.
|
|
|
|
// We will do that later.
|
|
|
|
// FIXME: we should instead check for imblances of the retain/releases,
|
|
|
|
// and suggest annotations.
|
|
|
|
if (Ctx.getLocationContext()->getParent())
|
|
|
|
return;
|
|
|
|
|
2011-08-24 03:01:07 +08:00
|
|
|
B = state->get<RefBindings>();
|
|
|
|
SmallVector<SymbolRef, 10> Leaked;
|
|
|
|
|
|
|
|
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I)
|
2011-08-24 04:07:14 +08:00
|
|
|
state = handleSymbolDeath(state, I->first, I->second, Leaked);
|
2011-08-24 03:01:07 +08:00
|
|
|
|
2011-10-26 03:57:11 +08:00
|
|
|
processLeaks(state, Leaked, Bd, Ctx, Pred);
|
2011-08-24 03:01:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const ProgramPointTag *
|
2011-09-02 14:44:22 +08:00
|
|
|
RetainCountChecker::getDeadSymbolTag(SymbolRef sym) const {
|
2011-08-24 03:01:07 +08:00
|
|
|
const SimpleProgramPointTag *&tag = DeadSymbolTags[sym];
|
|
|
|
if (!tag) {
|
2012-02-05 10:13:05 +08:00
|
|
|
SmallString<64> buf;
|
2011-08-24 03:01:07 +08:00
|
|
|
llvm::raw_svector_ostream out(buf);
|
2011-12-06 02:58:11 +08:00
|
|
|
out << "RetainCountChecker : Dead Symbol : ";
|
|
|
|
sym->dumpToStream(out);
|
2011-08-24 03:01:07 +08:00
|
|
|
tag = new SimpleProgramPointTag(out.str());
|
|
|
|
}
|
|
|
|
return tag;
|
|
|
|
}
|
|
|
|
|
2011-09-02 14:44:22 +08:00
|
|
|
void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper,
|
|
|
|
CheckerContext &C) const {
|
2011-08-24 03:01:07 +08:00
|
|
|
ExplodedNode *Pred = C.getPredecessor();
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2011-08-24 03:01:07 +08:00
|
|
|
RefBindings B = state->get<RefBindings>();
|
|
|
|
|
|
|
|
// Update counts from autorelease pools
|
|
|
|
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
|
|
|
|
E = SymReaper.dead_end(); I != E; ++I) {
|
|
|
|
SymbolRef Sym = *I;
|
|
|
|
if (const RefVal *T = B.lookup(Sym)){
|
|
|
|
// Use the symbol as the tag.
|
|
|
|
// FIXME: This might not be as unique as we would like.
|
2011-10-06 07:44:11 +08:00
|
|
|
GenericNodeBuilderRefCount Bd(C, getDeadSymbolTag(Sym));
|
2011-10-26 03:57:11 +08:00
|
|
|
llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, C,
|
2011-08-24 04:07:14 +08:00
|
|
|
Sym, *T);
|
|
|
|
if (!state)
|
2011-08-24 03:01:07 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
B = state->get<RefBindings>();
|
|
|
|
SmallVector<SymbolRef, 10> Leaked;
|
|
|
|
|
|
|
|
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
|
|
|
|
E = SymReaper.dead_end(); I != E; ++I) {
|
|
|
|
if (const RefVal *T = B.lookup(*I))
|
|
|
|
state = handleSymbolDeath(state, *I, *T, Leaked);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2011-10-06 07:44:11 +08:00
|
|
|
GenericNodeBuilderRefCount Bd(C, this);
|
2011-10-26 03:57:11 +08:00
|
|
|
Pred = processLeaks(state, Leaked, Bd, C, Pred);
|
2011-08-24 03:01:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Did we cache out?
|
|
|
|
if (!Pred)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Now generate a new node that nukes the old bindings.
|
|
|
|
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);
|
2011-10-27 05:06:34 +08:00
|
|
|
C.addTransition(state, Pred);
|
2011-08-24 03:01:07 +08:00
|
|
|
}
|
|
|
|
|
2011-08-29 03:11:56 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Debug printing of refcount bindings and autorelease pools.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
static void PrintPool(raw_ostream &Out, SymbolRef Sym,
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef State) {
|
2011-08-29 03:11:56 +08:00
|
|
|
Out << ' ';
|
|
|
|
if (Sym)
|
2011-12-06 02:58:11 +08:00
|
|
|
Sym->dumpToStream(Out);
|
2011-08-29 03:11:56 +08:00
|
|
|
else
|
|
|
|
Out << "<pool>";
|
|
|
|
Out << ":{";
|
|
|
|
|
|
|
|
// Get the contents of the pool.
|
|
|
|
if (const ARCounts *Cnts = State->get<AutoreleasePoolContents>(Sym))
|
|
|
|
for (ARCounts::iterator I = Cnts->begin(), E = Cnts->end(); I != E; ++I)
|
|
|
|
Out << '(' << I.getKey() << ',' << I.getData() << ')';
|
|
|
|
|
|
|
|
Out << '}';
|
|
|
|
}
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
static bool UsesAutorelease(ProgramStateRef state) {
|
2011-08-29 03:11:56 +08:00
|
|
|
// A state uses autorelease if it allocated an autorelease pool or if it has
|
|
|
|
// objects in the caller's autorelease pool.
|
|
|
|
return !state->get<AutoreleaseStack>().isEmpty() ||
|
|
|
|
state->get<AutoreleasePoolContents>(SymbolRef());
|
|
|
|
}
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
|
2011-09-02 14:44:22 +08:00
|
|
|
const char *NL, const char *Sep) const {
|
2011-08-29 03:11:56 +08:00
|
|
|
|
|
|
|
RefBindings B = State->get<RefBindings>();
|
|
|
|
|
|
|
|
if (!B.isEmpty())
|
|
|
|
Out << Sep << NL;
|
|
|
|
|
|
|
|
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
|
|
|
|
Out << I->first << " : ";
|
|
|
|
I->second.print(Out);
|
|
|
|
Out << NL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print the autorelease stack.
|
|
|
|
if (UsesAutorelease(State)) {
|
|
|
|
Out << Sep << NL << "AR pool stack:";
|
|
|
|
ARStack Stack = State->get<AutoreleaseStack>();
|
|
|
|
|
|
|
|
PrintPool(Out, SymbolRef(), State); // Print the caller's pool.
|
|
|
|
for (ARStack::iterator I = Stack.begin(), E = Stack.end(); I != E; ++I)
|
|
|
|
PrintPool(Out, *I, State);
|
|
|
|
|
|
|
|
Out << NL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-11 14:39:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2011-09-02 14:44:22 +08:00
|
|
|
// Checker registration.
|
2008-03-11 14:39:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2011-09-02 13:55:19 +08:00
|
|
|
void ento::registerRetainCountChecker(CheckerManager &Mgr) {
|
2011-09-02 14:44:22 +08:00
|
|
|
Mgr.registerChecker<RetainCountChecker>();
|
2011-09-02 13:55:19 +08:00
|
|
|
}
|
|
|
|
|