[analyzer] Drop support for GC mode in RetainCountChecker

A lot of code in RetainCountChecker deals with GC mode.
Given that GC mode is deprecated, Apple does not ship runtime for it,
and modern compiler toolchain does not support it, it makes sense to
remove the code dealing with it in order to aid understanding of
RetainCountChecker.

Differential Revision: https://reviews.llvm.org/D50747

llvm-svn: 340091
This commit is contained in:
George Karpenkov 2018-08-17 21:40:38 +00:00
parent ea4b476a30
commit 7390ddc968
10 changed files with 1343 additions and 3725 deletions

View File

@ -65,10 +65,6 @@ enum ArgEffect {
/// if CFRetain has been called on the argument. /// if CFRetain has been called on the argument.
IncRef, IncRef,
/// The argument acts as if has been passed to CFMakeCollectable, which
/// transfers the object to the Garbage Collector under GC.
MakeCollectable,
/// The argument is a pointer to a retain-counted object; on exit, the new /// The argument is a pointer to a retain-counted object; on exit, the new
/// value of the pointer is a +0 value or NULL. /// value of the pointer is a +0 value or NULL.
UnretainedOutParameter, UnretainedOutParameter,

View File

@ -83,14 +83,12 @@ public:
ReturnedNotOwned, // Return object does not pass ownership to caller. ReturnedNotOwned, // Return object does not pass ownership to caller.
ERROR_START, ERROR_START,
ErrorDeallocNotOwned, // -dealloc called on non-owned object. ErrorDeallocNotOwned, // -dealloc called on non-owned object.
ErrorDeallocGC, // Calling -dealloc with GC enabled.
ErrorUseAfterRelease, // Object used after released. ErrorUseAfterRelease, // Object used after released.
ErrorReleaseNotOwned, // Release of an object that was not owned. ErrorReleaseNotOwned, // Release of an object that was not owned.
ERROR_LEAK_START, ERROR_LEAK_START,
ErrorLeak, // A memory leak due to excessive reference counts. ErrorLeak, // A memory leak due to excessive reference counts.
ErrorLeakReturned, // A memory leak due to the returning method not having ErrorLeakReturned, // A memory leak due to the returning method not having
// the correct naming conventions. // the correct naming conventions.
ErrorGCLeakReturned,
ErrorOverAutorelease, ErrorOverAutorelease,
ErrorReturnedNotOwned ErrorReturnedNotOwned
}; };
@ -303,10 +301,6 @@ void RefVal::print(raw_ostream &Out) const {
Out << "Released"; Out << "Released";
break; break;
case ErrorDeallocGC:
Out << "-dealloc (GC)";
break;
case ErrorDeallocNotOwned: case ErrorDeallocNotOwned:
Out << "-dealloc (not-owned)"; Out << "-dealloc (not-owned)";
break; break;
@ -319,10 +313,6 @@ void RefVal::print(raw_ostream &Out) const {
Out << "Leaked (Bad naming)"; Out << "Leaked (Bad naming)";
break; break;
case ErrorGCLeakReturned:
Out << "Leaked (GC-ed at return)";
break;
case ErrorUseAfterRelease: case ErrorUseAfterRelease:
Out << "Use-After-Release [ERROR]"; Out << "Use-After-Release [ERROR]";
break; break;
@ -600,9 +590,6 @@ class RetainSummaryManager {
/// Ctx - The ASTContext object for the analyzed ASTs. /// Ctx - The ASTContext object for the analyzed ASTs.
ASTContext &Ctx; ASTContext &Ctx;
/// GCEnabled - Records whether or not the analyzed code runs in GC mode.
const bool GCEnabled;
/// Records whether or not the analyzed code runs in ARC mode. /// Records whether or not the analyzed code runs in ARC mode.
const bool ARCEnabled; const bool ARCEnabled;
@ -646,7 +633,7 @@ class RetainSummaryManager {
/// data in ScratchArgs. /// data in ScratchArgs.
ArgEffects getArgEffects(); ArgEffects getArgEffects();
enum UnaryFuncKind { cfretain, cfrelease, cfautorelease, cfmakecollectable }; enum UnaryFuncKind { cfretain, cfrelease, cfautorelease };
const RetainSummary *getUnarySummary(const FunctionType* FT, const RetainSummary *getUnarySummary(const FunctionType* FT,
UnaryFuncKind func); UnaryFuncKind func);
@ -732,19 +719,14 @@ private:
public: public:
RetainSummaryManager(ASTContext &ctx, bool gcenabled, bool usesARC) RetainSummaryManager(ASTContext &ctx, bool usesARC)
: Ctx(ctx), : Ctx(ctx),
GCEnabled(gcenabled),
ARCEnabled(usesARC), ARCEnabled(usesARC),
AF(BPAlloc), ScratchArgs(AF.getEmptyMap()), AF(BPAlloc), ScratchArgs(AF.getEmptyMap()),
ObjCAllocRetE(gcenabled ObjCAllocRetE(usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC)
? RetEffect::MakeGCNotOwned() : RetEffect::MakeOwned(RetEffect::ObjC)),
: (usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC) ObjCInitRetE(usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC)
: RetEffect::MakeOwned(RetEffect::ObjC))), : RetEffect::MakeOwnedWhenTrackedReceiver()) {
ObjCInitRetE(gcenabled
? RetEffect::MakeGCNotOwned()
: (usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC)
: RetEffect::MakeOwnedWhenTrackedReceiver())) {
InitializeClassMethodSummaries(); InitializeClassMethodSummaries();
InitializeMethodSummaries(); InitializeMethodSummaries();
} }
@ -802,12 +784,8 @@ public:
void updateSummaryForCall(const RetainSummary *&Summ, void updateSummaryForCall(const RetainSummary *&Summ,
const CallEvent &Call); const CallEvent &Call);
bool isGCEnabled() const { return GCEnabled; }
bool isARCEnabled() const { return ARCEnabled; } bool isARCEnabled() const { return ARCEnabled; }
bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; }
RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
friend class RetainSummaryTemplate; friend class RetainSummaryTemplate;
@ -895,12 +873,6 @@ static bool isAutorelease(const FunctionDecl *FD, StringRef FName) {
FName.endswith_lower("autorelease"); FName.endswith_lower("autorelease");
} }
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_lower("MakeCollectable") != StringRef::npos;
}
static ArgEffect getStopTrackingHardEquivalent(ArgEffect E) { static ArgEffect getStopTrackingHardEquivalent(ArgEffect E) {
switch (E) { switch (E) {
case DoNothing: case DoNothing:
@ -908,7 +880,6 @@ static ArgEffect getStopTrackingHardEquivalent(ArgEffect E) {
case DecRefBridgedTransferred: case DecRefBridgedTransferred:
case IncRef: case IncRef:
case IncRefMsg: case IncRefMsg:
case MakeCollectable:
case UnretainedOutParameter: case UnretainedOutParameter:
case RetainedOutParameter: case RetainedOutParameter:
case MayEscape: case MayEscape:
@ -1073,14 +1044,6 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) {
// Part of: <rdar://problem/7299394> and <rdar://problem/11282706>. // Part of: <rdar://problem/7299394> and <rdar://problem/11282706>.
// This will be addressed better with IPA. // This will be addressed better with IPA.
S = getPersistentStopSummary(); S = getPersistentStopSummary();
} else if (FName == "NSMakeCollectable") {
// Handle: id NSMakeCollectable(CFTypeRef)
S = (RetTy->isObjCIdType())
? getUnarySummary(FT, cfmakecollectable)
: getPersistentStopSummary();
// The headers on OS X 10.8 use cf_consumed/ns_returns_retained,
// but we can fully model NSMakeCollectable ourselves.
AllowAnnotations = false;
} else if (FName == "CFPlugInInstanceCreate") { } else if (FName == "CFPlugInInstanceCreate") {
S = getPersistentSummary(RetEffect::MakeNoRet()); S = getPersistentSummary(RetEffect::MakeNoRet());
} else if (FName == "IORegistryEntrySearchCFProperty" } else if (FName == "IORegistryEntrySearchCFProperty"
@ -1181,9 +1144,6 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) {
// The headers use cf_consumed, but we can fully model CFAutorelease // The headers use cf_consumed, but we can fully model CFAutorelease
// ourselves. // ourselves.
AllowAnnotations = false; AllowAnnotations = false;
} else if (isMakeCollectable(FD, FName)) {
S = getUnarySummary(FT, cfmakecollectable);
AllowAnnotations = false;
} else { } else {
S = getCFCreateGetRuleSummary(FD); S = getCFCreateGetRuleSummary(FD);
} }
@ -1294,7 +1254,6 @@ RetainSummaryManager::getUnarySummary(const FunctionType* FT,
case cfretain: Effect = IncRef; break; case cfretain: Effect = IncRef; break;
case cfrelease: Effect = DecRef; break; case cfrelease: Effect = DecRef; break;
case cfautorelease: Effect = Autorelease; break; case cfautorelease: Effect = Autorelease; break;
case cfmakecollectable: Effect = MakeCollectable; break;
} }
ScratchArgs = AF.add(ScratchArgs, 0, Effect); ScratchArgs = AF.add(ScratchArgs, 0, Effect);
@ -1732,16 +1691,6 @@ namespace {
} }
}; };
class DeallocGC : public CFRefBug {
public:
DeallocGC(const CheckerBase *checker)
: CFRefBug(checker, "-dealloc called while using garbage collection") {}
const char *getDescription() const override {
return "-dealloc called while using garbage collection";
}
};
class DeallocNotOwned : public CFRefBug { class DeallocNotOwned : public CFRefBug {
public: public:
DeallocNotOwned(const CheckerBase *checker) DeallocNotOwned(const CheckerBase *checker)
@ -1792,11 +1741,10 @@ namespace {
protected: protected:
SymbolRef Sym; SymbolRef Sym;
const SummaryLogTy &SummaryLog; const SummaryLogTy &SummaryLog;
bool GCEnabled;
public: public:
CFRefReportVisitor(SymbolRef sym, bool gcEnabled, const SummaryLogTy &log) CFRefReportVisitor(SymbolRef sym, const SummaryLogTy &log)
: Sym(sym), SummaryLog(log), GCEnabled(gcEnabled) {} : Sym(sym), SummaryLog(log) {}
void Profile(llvm::FoldingSetNodeID &ID) const override { void Profile(llvm::FoldingSetNodeID &ID) const override {
static int x = 0; static int x = 0;
@ -1816,9 +1764,9 @@ namespace {
class CFRefLeakReportVisitor : public CFRefReportVisitor { class CFRefLeakReportVisitor : public CFRefReportVisitor {
public: public:
CFRefLeakReportVisitor(SymbolRef sym, bool GCEnabled, CFRefLeakReportVisitor(SymbolRef sym,
const SummaryLogTy &log) const SummaryLogTy &log)
: CFRefReportVisitor(sym, GCEnabled, log) {} : CFRefReportVisitor(sym, log) {}
std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
const ExplodedNode *N, const ExplodedNode *N,
@ -1826,24 +1774,21 @@ namespace {
}; };
class CFRefReport : public BugReport { class CFRefReport : public BugReport {
void addGCModeDescription(const LangOptions &LOpts, bool GCEnabled);
public: public:
CFRefReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, CFRefReport(CFRefBug &D, const LangOptions &LOpts,
const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
bool registerVisitor = true) bool registerVisitor = true)
: BugReport(D, D.getDescription(), n) { : BugReport(D, D.getDescription(), n) {
if (registerVisitor) if (registerVisitor)
addVisitor(llvm::make_unique<CFRefReportVisitor>(sym, GCEnabled, Log)); addVisitor(llvm::make_unique<CFRefReportVisitor>(sym, Log));
addGCModeDescription(LOpts, GCEnabled);
} }
CFRefReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, CFRefReport(CFRefBug &D, const LangOptions &LOpts,
const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
StringRef endText) StringRef endText)
: BugReport(D, D.getDescription(), endText, n) { : BugReport(D, D.getDescription(), endText, n) {
addVisitor(llvm::make_unique<CFRefReportVisitor>(sym, GCEnabled, Log)); addVisitor(llvm::make_unique<CFRefReportVisitor>(sym, Log));
addGCModeDescription(LOpts, GCEnabled);
} }
llvm::iterator_range<ranges_iterator> getRanges() override { llvm::iterator_range<ranges_iterator> getRanges() override {
@ -1863,10 +1808,10 @@ namespace {
// Finds the location where a leak warning for 'sym' should be raised. // Finds the location where a leak warning for 'sym' should be raised.
void deriveAllocLocation(CheckerContext &Ctx, SymbolRef sym); void deriveAllocLocation(CheckerContext &Ctx, SymbolRef sym);
// Produces description of a leak warning which is printed on the console. // Produces description of a leak warning which is printed on the console.
void createDescription(CheckerContext &Ctx, bool GCEnabled, bool IncludeAllocationLine); void createDescription(CheckerContext &Ctx, bool IncludeAllocationLine);
public: public:
CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
CheckerContext &Ctx, CheckerContext &Ctx,
bool IncludeAllocationLine); bool IncludeAllocationLine);
@ -1878,39 +1823,6 @@ namespace {
}; };
} // end anonymous namespace } // end anonymous namespace
void CFRefReport::addGCModeDescription(const LangOptions &LOpts,
bool GCEnabled) {
const char *GCModeDescription = nullptr;
switch (LOpts.getGC()) {
case LangOptions::GCOnly:
assert(GCEnabled);
GCModeDescription = "Code is compiled to only use garbage collection";
break;
case LangOptions::NonGC:
assert(!GCEnabled);
GCModeDescription = "Code is compiled to use reference counts";
break;
case LangOptions::HybridGC:
if (GCEnabled) {
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;
}
}
assert(GCModeDescription && "invalid/unknown GC mode");
addExtraText(GCModeDescription);
}
static bool isNumericLiteralExpression(const Expr *E) { static bool isNumericLiteralExpression(const Expr *E) {
// FIXME: This set of cases was copied from SemaExprObjC. // FIXME: This set of cases was copied from SemaExprObjC.
return isa<IntegerLiteral>(E) || return isa<IntegerLiteral>(E) ||
@ -2047,14 +1959,7 @@ CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN,
if (CurrV.isOwned()) { if (CurrV.isOwned()) {
os << "+1 retain count"; os << "+1 retain count";
} else {
if (GCEnabled) {
assert(CurrV.getObjKind() == RetEffect::CF);
os << ". "
"Core Foundation objects are not automatically garbage collected.";
}
}
else {
assert (CurrV.isNotOwned()); assert (CurrV.isNotOwned());
os << "+0 retain count"; os << "+0 retain count";
} }
@ -2091,14 +1996,14 @@ CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN,
// We have an argument. Get the effect! // We have an argument. Get the effect!
AEffects.push_back(Summ->getArg(i)); AEffects.push_back(Summ->getArg(i));
} }
} } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { if (const Expr *receiver = ME->getInstanceReceiver()) {
if (const Expr *receiver = ME->getInstanceReceiver())
if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx) if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
.getAsLocSymbol() == Sym) { .getAsLocSymbol() == Sym) {
// The symbol we are tracking is the receiver. // The symbol we are tracking is the receiver.
AEffects.push_back(Summ->getReceiverEffect()); AEffects.push_back(Summ->getReceiverEffect());
} }
}
} }
} }
@ -2107,7 +2012,7 @@ CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN,
RefVal PrevV = *PrevT; RefVal PrevV = *PrevT;
// Specially handle -dealloc. // Specially handle -dealloc.
if (!GCEnabled && std::find(AEffects.begin(), AEffects.end(), Dealloc) != if (std::find(AEffects.begin(), AEffects.end(), Dealloc) !=
AEffects.end()) { AEffects.end()) {
// Determine if the object's reference count was pushed to zero. // Determine if the object's reference count was pushed to zero.
assert(!PrevV.hasSameState(CurrV) && "The state should have changed."); assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");
@ -2120,41 +2025,6 @@ CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN,
} }
} }
// Specially handle CFMakeCollectable and friends.
if (std::find(AEffects.begin(), AEffects.end(), MakeCollectable) !=
AEffects.end()) {
// Get the name of the function.
const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
SVal X =
CurrSt->getSValAsScalarOrLoc(cast<CallExpr>(S)->getCallee(), LCtx);
const FunctionDecl *FD = X.getAsFunctionDecl();
if (GCEnabled) {
// Determine if the object's reference count was pushed to zero.
assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");
os << "In GC mode a call to '" << *FD
<< "' decrements an object's retain count and registers the "
"object with the garbage collector. ";
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()
<< '.';
}
else
os << "When GC is not enabled a call to '" << *FD
<< "' has no effect on its argument.";
// Nothing more to say.
break;
}
// Determine if the typestate has changed. // Determine if the typestate has changed.
if (!PrevV.hasSameState(CurrV)) if (!PrevV.hasSameState(CurrV))
switch (CurrV.getKind()) { switch (CurrV.getKind()) {
@ -2178,12 +2048,6 @@ CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN,
if (unsigned Count = CurrV.getCount()) if (unsigned Count = CurrV.getCount())
os << " The object now has a +" << Count << " retain count."; os << " The object now has a +" << Count << " retain count.";
if (PrevV.getKind() == RefVal::Released) {
assert(GCEnabled && CurrV.getCount() > 0);
os << " The object is not eligible for garbage collection until "
"the retain count reaches 0 again.";
}
break; break;
case RefVal::Released: case RefVal::Released:
@ -2211,26 +2075,6 @@ CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN,
default: default:
return nullptr; return nullptr;
} }
// Emit any remaining diagnostics for the argument effects (if any).
for (SmallVectorImpl<ArgEffect>::iterator I=AEffects.begin(),
E=AEffects.end(); I != E; ++I) {
// A bunch of things have alternate behavior under GC.
if (GCEnabled)
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;
}
}
} while (0); } while (0);
if (os.str().empty()) if (os.str().empty())
@ -2437,14 +2281,6 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
} }
} }
} }
else if (RV->getKind() == RefVal::ErrorGCLeakReturned) {
const ObjCMethodDecl &MD = cast<ObjCMethodDecl>(EndN->getCodeDecl());
os << " and returned from method '" << MD.getSelector().getAsString()
<< "' 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";
}
else else
os << " is not referenced later in this execution path and has a retain " os << " is not referenced later in this execution path and has a retain "
"count of +" << RV->getCount(); "count of +" << RV->getCount();
@ -2512,15 +2348,12 @@ void CFRefLeakReport::deriveAllocLocation(CheckerContext &Ctx,SymbolRef sym) {
UniqueingDecl = AllocNode->getLocationContext()->getDecl(); UniqueingDecl = AllocNode->getLocationContext()->getDecl();
} }
void CFRefLeakReport::createDescription(CheckerContext &Ctx, bool GCEnabled, void CFRefLeakReport::createDescription(CheckerContext &Ctx,
bool IncludeAllocationLine) { bool IncludeAllocationLine) {
assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid()); assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
Description.clear(); Description.clear();
llvm::raw_string_ostream os(Description); llvm::raw_string_ostream os(Description);
os << "Potential leak "; os << "Potential leak of an object";
if (GCEnabled)
os << "(when using garbage collection) ";
os << "of an object";
Optional<std::string> RegionDescription = describeRegion(AllocBinding); Optional<std::string> RegionDescription = describeRegion(AllocBinding);
if (RegionDescription) { if (RegionDescription) {
@ -2533,19 +2366,19 @@ void CFRefLeakReport::createDescription(CheckerContext &Ctx, bool GCEnabled,
} }
CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
bool GCEnabled, const SummaryLogTy &Log, const SummaryLogTy &Log,
ExplodedNode *n, SymbolRef sym, ExplodedNode *n, SymbolRef sym,
CheckerContext &Ctx, CheckerContext &Ctx,
bool IncludeAllocationLine) bool IncludeAllocationLine)
: CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) { : CFRefReport(D, LOpts, Log, n, sym, false) {
deriveAllocLocation(Ctx, sym); deriveAllocLocation(Ctx, sym);
if (!AllocBinding) if (!AllocBinding)
deriveParamLocation(Ctx, sym); deriveParamLocation(Ctx, sym);
createDescription(Ctx, GCEnabled, IncludeAllocationLine); createDescription(Ctx, IncludeAllocationLine);
addVisitor(llvm::make_unique<CFRefLeakReportVisitor>(sym, GCEnabled, Log)); addVisitor(llvm::make_unique<CFRefLeakReportVisitor>(sym, Log));
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -2571,10 +2404,9 @@ class RetainCountChecker
eval::Assume, eval::Assume,
eval::Call > { eval::Call > {
mutable std::unique_ptr<CFRefBug> useAfterRelease, releaseNotOwned; mutable std::unique_ptr<CFRefBug> useAfterRelease, releaseNotOwned;
mutable std::unique_ptr<CFRefBug> deallocGC, deallocNotOwned; mutable std::unique_ptr<CFRefBug> deallocNotOwned;
mutable std::unique_ptr<CFRefBug> overAutorelease, returnNotOwnedForOwned; mutable std::unique_ptr<CFRefBug> overAutorelease, returnNotOwnedForOwned;
mutable std::unique_ptr<CFRefBug> leakWithinFunction, leakAtReturn; mutable std::unique_ptr<CFRefBug> leakWithinFunction, leakAtReturn;
mutable std::unique_ptr<CFRefBug> leakWithinFunctionGC, leakAtReturnGC;
typedef llvm::DenseMap<SymbolRef, const CheckerProgramPointTag *> SymbolTagMap; typedef llvm::DenseMap<SymbolRef, const CheckerProgramPointTag *> SymbolTagMap;
@ -2582,7 +2414,6 @@ class RetainCountChecker
mutable SymbolTagMap DeadSymbolTags; mutable SymbolTagMap DeadSymbolTags;
mutable std::unique_ptr<RetainSummaryManager> Summaries; mutable std::unique_ptr<RetainSummaryManager> Summaries;
mutable std::unique_ptr<RetainSummaryManager> SummariesGC;
mutable SummaryLogTy SummaryLog; mutable SummaryLogTy SummaryLog;
mutable bool ShouldResetSummaryLog; mutable bool ShouldResetSummaryLog;
@ -2633,72 +2464,31 @@ public:
ShouldResetSummaryLog = !SummaryLog.empty(); ShouldResetSummaryLog = !SummaryLog.empty();
} }
CFRefBug *getLeakWithinFunctionBug(const LangOptions &LOpts, CFRefBug *getLeakWithinFunctionBug(const LangOptions &LOpts) const {
bool GCEnabled) const { if (!leakWithinFunction)
if (GCEnabled) { leakWithinFunction.reset(new Leak(this, "Leak"));
if (!leakWithinFunctionGC) return leakWithinFunction.get();
leakWithinFunctionGC.reset(new Leak(this, "Leak of object when using "
"garbage collection"));
return leakWithinFunctionGC.get();
} else {
if (!leakWithinFunction) {
if (LOpts.getGC() == LangOptions::HybridGC) {
leakWithinFunction.reset(new Leak(this,
"Leak of object when not using "
"garbage collection (GC) in "
"dual GC/non-GC code"));
} else {
leakWithinFunction.reset(new Leak(this, "Leak"));
}
}
return leakWithinFunction.get();
}
} }
CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts, bool GCEnabled) const { CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts) const {
if (GCEnabled) { if (!leakAtReturn)
if (!leakAtReturnGC) leakAtReturn.reset(new Leak(this, "Leak of returned object"));
leakAtReturnGC.reset(new Leak(this,
"Leak of returned object when using "
"garbage collection"));
return leakAtReturnGC.get();
} else {
if (!leakAtReturn) {
if (LOpts.getGC() == LangOptions::HybridGC) {
leakAtReturn.reset(new Leak(this,
"Leak of returned object when not using "
"garbage collection (GC) in dual "
"GC/non-GC code"));
} else {
leakAtReturn.reset(new Leak(this, "Leak of returned object"));
}
}
return leakAtReturn.get(); return leakAtReturn.get();
}
} }
RetainSummaryManager &getSummaryManager(ASTContext &Ctx, RetainSummaryManager &getSummaryManager(ASTContext &Ctx) const {
bool GCEnabled) const {
// FIXME: We don't support ARC being turned on and off during one analysis. // FIXME: We don't support ARC being turned on and off during one analysis.
// (nor, for that matter, do we support changing ASTContexts) // (nor, for that matter, do we support changing ASTContexts)
bool ARCEnabled = (bool)Ctx.getLangOpts().ObjCAutoRefCount; bool ARCEnabled = (bool)Ctx.getLangOpts().ObjCAutoRefCount;
if (GCEnabled) { if (!Summaries)
if (!SummariesGC) Summaries.reset(new RetainSummaryManager(Ctx, ARCEnabled));
SummariesGC.reset(new RetainSummaryManager(Ctx, true, ARCEnabled)); else
else assert(Summaries->isARCEnabled() == ARCEnabled);
assert(SummariesGC->isARCEnabled() == ARCEnabled); return *Summaries;
return *SummariesGC;
} else {
if (!Summaries)
Summaries.reset(new RetainSummaryManager(Ctx, false, ARCEnabled));
else
assert(Summaries->isARCEnabled() == ARCEnabled);
return *Summaries;
}
} }
RetainSummaryManager &getSummaryManager(CheckerContext &C) const { RetainSummaryManager &getSummaryManager(CheckerContext &C) const {
return getSummaryManager(C.getASTContext(), C.isObjCGCEnabled()); return getSummaryManager(C.getASTContext());
} }
void printState(raw_ostream &Out, ProgramStateRef State, void printState(raw_ostream &Out, ProgramStateRef State,
@ -3177,7 +2967,6 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
break; break;
} }
case RetEffect::GCNotOwnedSymbol:
case RetEffect::NotOwnedSymbol: { case RetEffect::NotOwnedSymbol: {
const Expr *Ex = CallOrMsg.getOriginExpr(); const Expr *Ex = CallOrMsg.getOriginExpr();
SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol(); SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol();
@ -3217,12 +3006,7 @@ ProgramStateRef
RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
RefVal V, ArgEffect E, RefVal::Kind &hasErr, RefVal V, ArgEffect E, RefVal::Kind &hasErr,
CheckerContext &C) const { CheckerContext &C) const {
// In GC mode [... release] and [... retain] do nothing. bool IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount;
// In ARC mode they shouldn't exist at all, but we just ignore them.
bool IgnoreRetainMsg = C.isObjCGCEnabled();
if (!IgnoreRetainMsg)
IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount;
switch (E) { switch (E) {
default: default:
break; break;
@ -3230,18 +3014,15 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
E = IgnoreRetainMsg ? DoNothing : IncRef; E = IgnoreRetainMsg ? DoNothing : IncRef;
break; break;
case DecRefMsg: case DecRefMsg:
E = IgnoreRetainMsg ? DoNothing : DecRef; E = IgnoreRetainMsg ? DoNothing: DecRef;
break; break;
case DecRefMsgAndStopTrackingHard: case DecRefMsgAndStopTrackingHard:
E = IgnoreRetainMsg ? StopTracking : DecRefAndStopTrackingHard; E = IgnoreRetainMsg ? StopTracking : DecRefAndStopTrackingHard;
break; break;
case MakeCollectable:
E = C.isObjCGCEnabled() ? DecRef : DoNothing;
break;
} }
// Handle all use-after-releases. // Handle all use-after-releases.
if (!C.isObjCGCEnabled() && V.getKind() == RefVal::Released) { if (V.getKind() == RefVal::Released) {
V = V ^ RefVal::ErrorUseAfterRelease; V = V ^ RefVal::ErrorUseAfterRelease;
hasErr = V.getKind(); hasErr = V.getKind();
return setRefBinding(state, sym, V); return setRefBinding(state, sym, V);
@ -3250,9 +3031,8 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
switch (E) { switch (E) {
case DecRefMsg: case DecRefMsg:
case IncRefMsg: case IncRefMsg:
case MakeCollectable:
case DecRefMsgAndStopTrackingHard: case DecRefMsgAndStopTrackingHard:
llvm_unreachable("DecRefMsg/IncRefMsg/MakeCollectable already converted"); llvm_unreachable("DecRefMsg/IncRefMsg already converted");
case UnretainedOutParameter: case UnretainedOutParameter:
case RetainedOutParameter: case RetainedOutParameter:
@ -3260,13 +3040,6 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
"not have ref state."); "not have ref state.");
case Dealloc: case Dealloc:
// Any use of -dealloc in GC is *bad*.
if (C.isObjCGCEnabled()) {
V = V ^ RefVal::ErrorDeallocGC;
hasErr = V.getKind();
break;
}
switch (V.getKind()) { switch (V.getKind()) {
default: default:
llvm_unreachable("Invalid RefVal state for an explicit dealloc."); llvm_unreachable("Invalid RefVal state for an explicit dealloc.");
@ -3294,8 +3067,6 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
return state; return state;
case Autorelease: case Autorelease:
if (C.isObjCGCEnabled())
return state;
// Update the autorelease counts. // Update the autorelease counts.
V = V.autorelease(); V = V.autorelease();
break; break;
@ -3312,11 +3083,6 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
case RefVal::NotOwned: case RefVal::NotOwned:
V = V + 1; V = V + 1;
break; break;
case RefVal::Released:
// Non-GC cases are handled above.
assert(C.isObjCGCEnabled());
V = (V ^ RefVal::Owned) + 1;
break;
} }
break; break;
@ -3361,13 +3127,6 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
hasErr = V.getKind(); hasErr = V.getKind();
} }
break; break;
case RefVal::Released:
// Non-GC cases are handled above.
assert(C.isObjCGCEnabled());
V = V ^ RefVal::ErrorUseAfterRelease;
hasErr = V.getKind();
break;
} }
break; break;
} }
@ -3407,11 +3166,6 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St,
releaseNotOwned.reset(new BadRelease(this)); releaseNotOwned.reset(new BadRelease(this));
BT = releaseNotOwned.get(); BT = releaseNotOwned.get();
break; break;
case RefVal::ErrorDeallocGC:
if (!deallocGC)
deallocGC.reset(new DeallocGC(this));
BT = deallocGC.get();
break;
case RefVal::ErrorDeallocNotOwned: case RefVal::ErrorDeallocNotOwned:
if (!deallocNotOwned) if (!deallocNotOwned)
deallocNotOwned.reset(new DeallocNotOwned(this)); deallocNotOwned.reset(new DeallocNotOwned(this));
@ -3421,7 +3175,7 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St,
assert(BT); assert(BT);
auto report = std::unique_ptr<BugReport>( auto report = std::unique_ptr<BugReport>(
new CFRefReport(*BT, C.getASTContext().getLangOpts(), C.isObjCGCEnabled(), new CFRefReport(*BT, C.getASTContext().getLangOpts(),
SummaryLog, N, Sym)); SummaryLog, N, Sym));
report->addRange(ErrorRange); report->addRange(ErrorRange);
C.emitReport(std::move(report)); C.emitReport(std::move(report));
@ -3443,7 +3197,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
return false; return false;
// For now, we're only handling the functions that return aliases of their // For now, we're only handling the functions that return aliases of their
// arguments: CFRetain and CFMakeCollectable (and their families). // arguments: CFRetain (and its families).
// Eventually we should add other functions we can model entirely, // Eventually we should add other functions we can model entirely,
// such as CFRelease, which don't invalidate their arguments or globals. // such as CFRelease, which don't invalidate their arguments or globals.
if (CE->getNumArgs() != 1) if (CE->getNumArgs() != 1)
@ -3460,19 +3214,14 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
bool hasTrustedImplementationAnnotation = false; bool hasTrustedImplementationAnnotation = false;
QualType ResultTy = CE->getCallReturnType(C.getASTContext()); QualType ResultTy = CE->getCallReturnType(C.getASTContext());
if (ResultTy->isObjCIdType()) { if (ResultTy->isPointerType()) {
// Handle: id NSMakeCollectable(CFTypeRef)
canEval = II->isStr("NSMakeCollectable");
} else if (ResultTy->isPointerType()) {
// Handle: (CF|CG|CV)Retain // Handle: (CF|CG|CV)Retain
// CFAutorelease // CFAutorelease
// CFMakeCollectable // It's okay to be a little sloppy here.
// It's okay to be a little sloppy here (CGMakeCollectable doesn't exist).
if (cocoa::isRefType(ResultTy, "CF", FName) || if (cocoa::isRefType(ResultTy, "CF", FName) ||
cocoa::isRefType(ResultTy, "CG", FName) || cocoa::isRefType(ResultTy, "CG", FName) ||
cocoa::isRefType(ResultTy, "CV", FName)) { cocoa::isRefType(ResultTy, "CV", FName)) {
canEval = isRetain(FD, FName) || isAutorelease(FD, FName) || canEval = isRetain(FD, FName) || isAutorelease(FD, FName);
isMakeCollectable(FD, FName);
} else { } else {
if (FD->getDefinition()) { if (FD->getDefinition()) {
canEval = isTrustedReferenceCountImplementation(FD->getDefinition()); canEval = isTrustedReferenceCountImplementation(FD->getDefinition());
@ -3641,18 +3390,9 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
if (X.isReturnedOwned() && X.getCount() == 0) { if (X.isReturnedOwned() && X.getCount() == 0) {
if (RE.getKind() != RetEffect::NoRet) { if (RE.getKind() != RetEffect::NoRet) {
bool hasError = false; bool hasError = false;
if (C.isObjCGCEnabled() && RE.getObjKind() == RetEffect::ObjC) { if (!RE.isOwned()) {
// Things are more complicated with garbage collection. If the // The returning type is a CF, we expect the enclosing method should
// returned object is suppose to be an Objective-C object, we have // return ownership.
// 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; hasError = true;
X = X ^ RefVal::ErrorLeakReturned; X = X ^ RefVal::ErrorLeakReturned;
} }
@ -3665,9 +3405,8 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag); ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag);
if (N) { if (N) {
const LangOptions &LOpts = C.getASTContext().getLangOpts(); const LangOptions &LOpts = C.getASTContext().getLangOpts();
bool GCEnabled = C.isObjCGCEnabled();
C.emitReport(std::unique_ptr<BugReport>(new CFRefLeakReport( C.emitReport(std::unique_ptr<BugReport>(new CFRefLeakReport(
*getLeakAtReturnBug(LOpts, GCEnabled), LOpts, GCEnabled, *getLeakAtReturnBug(LOpts), LOpts,
SummaryLog, N, Sym, C, IncludeAllocationLine))); SummaryLog, N, Sym, C, IncludeAllocationLine)));
} }
} }
@ -3695,7 +3434,7 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
C.emitReport(std::unique_ptr<BugReport>(new CFRefReport( C.emitReport(std::unique_ptr<BugReport>(new CFRefReport(
*returnNotOwnedForOwned, C.getASTContext().getLangOpts(), *returnNotOwnedForOwned, C.getASTContext().getLangOpts(),
C.isObjCGCEnabled(), SummaryLog, N, Sym))); SummaryLog, N, Sym)));
} }
} }
} }
@ -3839,7 +3578,6 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
if (!ACnt) if (!ACnt)
return state; return state;
assert(!Ctx.isObjCGCEnabled() && "Autorelease counts in GC mode?");
unsigned Cnt = V.getCount(); unsigned Cnt = V.getCount();
// FIXME: Handle sending 'autorelease' to already released object. // FIXME: Handle sending 'autorelease' to already released object.
@ -3899,7 +3637,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
Ctx.emitReport(std::unique_ptr<BugReport>( Ctx.emitReport(std::unique_ptr<BugReport>(
new CFRefReport(*overAutorelease, LOpts, /* GCEnabled = */ false, new CFRefReport(*overAutorelease, LOpts,
SummaryLog, N, Sym, os.str()))); SummaryLog, N, Sym, os.str())));
} }
@ -3947,13 +3685,12 @@ RetainCountChecker::processLeaks(ProgramStateRef state,
I = Leaked.begin(), E = Leaked.end(); I != E; ++I) { I = Leaked.begin(), E = Leaked.end(); I != E; ++I) {
const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
bool GCEnabled = Ctx.isObjCGCEnabled(); CFRefBug *BT = Pred ? getLeakWithinFunctionBug(LOpts)
CFRefBug *BT = Pred ? getLeakWithinFunctionBug(LOpts, GCEnabled) : getLeakAtReturnBug(LOpts);
: getLeakAtReturnBug(LOpts, GCEnabled);
assert(BT && "BugType not initialized."); assert(BT && "BugType not initialized.");
Ctx.emitReport(std::unique_ptr<BugReport>( Ctx.emitReport(std::unique_ptr<BugReport>(
new CFRefLeakReport(*BT, LOpts, GCEnabled, SummaryLog, N, *I, Ctx, new CFRefLeakReport(*BT, LOpts, SummaryLog, N, *I, Ctx,
IncludeAllocationLine))); IncludeAllocationLine)));
} }
} }
@ -4130,7 +3867,7 @@ namespace objc_retain {
#define createCallEffect(D, KIND)\ #define createCallEffect(D, KIND)\
ASTContext &Ctx = D->getASTContext();\ ASTContext &Ctx = D->getASTContext();\
LangOptions L = Ctx.getLangOpts();\ LangOptions L = Ctx.getLangOpts();\
RetainSummaryManager M(Ctx, L.GCOnly, L.ObjCAutoRefCount);\ RetainSummaryManager M(Ctx, L.ObjCAutoRefCount);\
const RetainSummary *S = M.get ## KIND ## Summary(D);\ const RetainSummary *S = M.get ## KIND ## Summary(D);\
CallEffects CE(S->getRetEffect());\ CallEffects CE(S->getRetEffect());\
CE.Receiver = S->getReceiverEffect();\ CE.Receiver = S->getReceiverEffect();\

View File

@ -1,85 +0,0 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.RetainCount -analyzer-store=region -verify -fobjc-gc %s -Wno-implicit-function-declaration
//===----------------------------------------------------------------------===//
// The following code is reduced using delta-debugging from
// Foundation.h and CoreFoundation.h (Mac OS X).
//
// It includes the basic definitions for the test cases below.
// Not directly including [Core]Foundation.h directly makes this test case
// both svelte and portable to non-Mac platforms.
//===----------------------------------------------------------------------===//
typedef const void * CFTypeRef;
void CFRelease(CFTypeRef cf);
CFTypeRef CFRetain(CFTypeRef cf);
CFTypeRef CFMakeCollectable(CFTypeRef cf);
typedef const struct __CFAllocator * CFAllocatorRef;
typedef double CFTimeInterval;
typedef CFTimeInterval CFAbsoluteTime;
typedef const struct __CFDate * CFDateRef;
extern CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at);
extern CFAbsoluteTime CFDateGetAbsoluteTime(CFDateRef theDate);
typedef struct objc_object {} *id;
typedef signed char BOOL;
static __inline__ __attribute__((always_inline)) id NSMakeCollectable(CFTypeRef cf) { return 0; }
@protocol NSObject - (BOOL)isEqual:(id)object;
- (oneway void)release;
- (id)retain;
@end
@class NSArray;
//===----------------------------------------------------------------------===//
// Test cases.
//===----------------------------------------------------------------------===//
CFAbsoluteTime CFAbsoluteTimeGetCurrent();
CFAbsoluteTime f1_use_after_release() {
CFAbsoluteTime t = CFAbsoluteTimeGetCurrent();
CFDateRef date = CFDateCreate(0, t);
CFRetain(date);
[NSMakeCollectable(date) release];
CFDateGetAbsoluteTime(date); // no-warning
CFRelease(date);
t = CFDateGetAbsoluteTime(date); // expected-warning{{Reference-counted object is used after it is released}}
return t;
}
// The following two test cases verifies that CFMakeCollectable is a no-op
// in non-GC mode and a "release" in GC mode.
CFAbsoluteTime f2_use_after_release() {
CFAbsoluteTime t = CFAbsoluteTimeGetCurrent();
CFDateRef date = CFDateCreate(0, t);
CFRetain(date);
[(id) CFMakeCollectable(date) release];
CFDateGetAbsoluteTime(date); // no-warning
CFRelease(date);
t = CFDateGetAbsoluteTime(date); // expected-warning{{Reference-counted object is used after it is released}}
return t;
}
CFAbsoluteTime f2_noleak() {
CFAbsoluteTime t = CFAbsoluteTimeGetCurrent();
CFDateRef date = CFDateCreate(0, t);
CFRetain(date);
[(id) CFMakeCollectable(date) release];
CFDateGetAbsoluteTime(date); // no-warning
t = CFDateGetAbsoluteTime(date); // no-warning
CFRelease(date); // no-warning
return t;
}
void f3_leak_with_gc() {
CFDateRef date = CFDateCreate(0, CFAbsoluteTimeGetCurrent()); // expected-warning 2 {{leak}}
[[(id) date retain] release];
}
// The following test case verifies that we "stop tracking" a retained object
// when it is passed as an argument to an implicitly defined function.
CFAbsoluteTime f4() {
CFAbsoluteTime t = CFAbsoluteTimeGetCurrent();
CFDateRef date = CFDateCreate(0, t);
CFRetain(date);
some_implicitly_defined_function_stop_tracking(date); // no-warning
return t;
}

View File

@ -1,63 +0,0 @@
// RUN: %clang_analyze_cc1 -triple %itanium_abi_triple -analyzer-checker=core,osx.cocoa.RetainCount,alpha.core -analyzer-store=region -fobjc-gc -verify %s
typedef const void * CFTypeRef;
typedef const struct __CFString * CFStringRef;
typedef const struct __CFAllocator * CFAllocatorRef;
typedef const struct __CFDictionary * CFDictionaryRef;
CFTypeRef CFMakeCollectable(CFTypeRef cf) ;
extern CFStringRef CFStringCreateWithFormat(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef format, ...);
typedef signed char BOOL;
typedef unsigned int NSUInteger;
typedef struct _NSZone NSZone;
@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator;
@protocol NSObject
- (BOOL)isEqual:(id)object;
- (id)autorelease;
@end
@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end @protocol NSMutableCopying
- (id)mutableCopyWithZone:(NSZone *)zone;
@end
@protocol
NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder;
@end
@interface NSObject <NSObject> {}
- (id)init;
+ (id)alloc;
@end
enum { NSASCIIStringEncoding = 1, NSNEXTSTEPStringEncoding = 2, NSJapaneseEUCStringEncoding = 3, NSUTF8StringEncoding = 4, NSISOLatin1StringEncoding = 5, NSSymbolStringEncoding = 6, NSNonLossyASCIIStringEncoding = 7, NSShiftJISStringEncoding = 8, NSISOLatin2StringEncoding = 9, NSUnicodeStringEncoding = 10, NSWindowsCP1251StringEncoding = 11, NSWindowsCP1252StringEncoding = 12, NSWindowsCP1253StringEncoding = 13, NSWindowsCP1254StringEncoding = 14, NSWindowsCP1250StringEncoding = 15, NSISO2022JPStringEncoding = 21, NSMacOSRomanStringEncoding = 30, NSUTF16StringEncoding = NSUnicodeStringEncoding, NSUTF16BigEndianStringEncoding = 0x90000100, NSUTF16LittleEndianStringEncoding = 0x94000100, NSUTF32StringEncoding = 0x8c000100, NSUTF32BigEndianStringEncoding = 0x98000100, NSUTF32LittleEndianStringEncoding = 0x9c000100 };
typedef NSUInteger NSStringEncoding;
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding>
- (NSUInteger)length;
- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)len encoding:(NSStringEncoding)encoding freeWhenDone:(BOOL)freeBuffer;
@end
@interface NSAutoreleasePool : NSObject {}
- (void)drain;
@end
extern NSString * const NSXMLParserErrorDomain ;
// The actual test case. UTIL_AUTORELEASE_CF_AS_ID is a macro that doesn't
// actually do what it was intended to.
#define NSSTRINGWRAPPER(bytes,len) \
[[[NSString alloc] initWithBytesNoCopy: (void*)(bytes) length: (len) encoding: NSUTF8StringEncoding freeWhenDone: (BOOL)0] autorelease]
#define UTIL_AUTORELEASE_CF_AS_ID(cf) ( (((void*)0) == (cf)) ? ((void*)0) : [(id) CFMakeCollectable( (CFTypeRef) cf) autorelease] )
#define UTIL_AUTORELEASE_CF_AS_ID_WITHOUT_TEST(cf) ( [(id) CFMakeCollectable( (CFTypeRef) cf) autorelease] )
static char *lorem = "fooBarBaz";
void NSLog(NSString *, ...);
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *tmp1 = NSSTRINGWRAPPER(lorem, 6); // no-warning
NSString *tmp2 = UTIL_AUTORELEASE_CF_AS_ID( CFStringCreateWithFormat(((void*)0), ((void*)0), ((CFStringRef) __builtin___CFStringMakeConstantString ("" "lorem: %@" "")), tmp1) ); // expected-warning 2 {{leak}}
NSString *tmp3 = UTIL_AUTORELEASE_CF_AS_ID_WITHOUT_TEST( CFStringCreateWithFormat(((void*)0), ((void*)0), ((CFStringRef) __builtin___CFStringMakeConstantString ("" "lorem: %@" "")), tmp1) );
NSLog(@"tmp2: %@ tmp3: %@", tmp2, tmp3);
[pool drain];
return 0;
}

View File

@ -1,434 +0,0 @@
// RUN: %clang_analyze_cc1 -triple %itanium_abi_triple -analyzer-checker=core,osx.cocoa.RetainCount,osx.cocoa.NSAutoreleasePool -analyzer-store=region -fobjc-gc-only -fblocks -verify -Wno-objc-root-class %s
//===----------------------------------------------------------------------===//
// Header stuff.
//===----------------------------------------------------------------------===//
typedef unsigned int __darwin_natural_t;
typedef unsigned long uintptr_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef unsigned int UInt32;
typedef signed long CFIndex;
typedef struct {
CFIndex location;
CFIndex length;
} CFRange;
static __inline__ __attribute__((always_inline)) CFRange CFRangeMake(CFIndex loc, CFIndex len) {
CFRange range;
range.location = loc;
range.length = len;
return range;
}
typedef const void * CFTypeRef;
typedef const struct __CFString * CFStringRef;
typedef const struct __CFAllocator * CFAllocatorRef;
extern const CFAllocatorRef kCFAllocatorDefault;
extern CFTypeRef CFRetain(CFTypeRef cf);
extern void CFRelease(CFTypeRef cf);
typedef struct {
}
CFArrayCallBacks;
extern const CFArrayCallBacks kCFTypeArrayCallBacks;
typedef const struct __CFArray * CFArrayRef;
typedef struct __CFArray * CFMutableArrayRef;
extern CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFArrayCallBacks *callBacks);
extern const void *CFArrayGetValueAtIndex(CFArrayRef theArray, CFIndex idx);
extern void CFArrayAppendValue(CFMutableArrayRef theArray, const void *value);
typedef struct {
}
CFDictionaryKeyCallBacks;
extern const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks;
typedef struct {
}
CFDictionaryValueCallBacks;
extern const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks;
typedef const struct __CFDictionary * CFDictionaryRef;
typedef struct __CFDictionary * CFMutableDictionaryRef;
extern CFMutableDictionaryRef CFDictionaryCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks);
typedef UInt32 CFStringEncoding;
enum {
kCFStringEncodingMacRoman = 0, kCFStringEncodingWindowsLatin1 = 0x0500, kCFStringEncodingISOLatin1 = 0x0201, kCFStringEncodingNextStepLatin = 0x0B01, kCFStringEncodingASCII = 0x0600, kCFStringEncodingUnicode = 0x0100, kCFStringEncodingUTF8 = 0x08000100, kCFStringEncodingNonLossyASCII = 0x0BFF , kCFStringEncodingUTF16 = 0x0100, kCFStringEncodingUTF16BE = 0x10000100, kCFStringEncodingUTF16LE = 0x14000100, kCFStringEncodingUTF32 = 0x0c000100, kCFStringEncodingUTF32BE = 0x18000100, kCFStringEncodingUTF32LE = 0x1c000100 };
extern CFStringRef CFStringCreateWithCString(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding);
typedef double CFTimeInterval;
typedef CFTimeInterval CFAbsoluteTime;
extern CFAbsoluteTime CFAbsoluteTimeGetCurrent(void);
typedef const struct __CFDate * CFDateRef;
extern CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at);
extern CFAbsoluteTime CFDateGetAbsoluteTime(CFDateRef theDate);
typedef __darwin_natural_t natural_t;
typedef natural_t mach_port_name_t;
typedef mach_port_name_t mach_port_t;
typedef int kern_return_t;
typedef kern_return_t mach_error_t;
enum {
kCFNumberSInt8Type = 1, kCFNumberSInt16Type = 2, kCFNumberSInt32Type = 3, kCFNumberSInt64Type = 4, kCFNumberFloat32Type = 5, kCFNumberFloat64Type = 6, kCFNumberCharType = 7, kCFNumberShortType = 8, kCFNumberIntType = 9, kCFNumberLongType = 10, kCFNumberLongLongType = 11, kCFNumberFloatType = 12, kCFNumberDoubleType = 13, kCFNumberCFIndexType = 14, kCFNumberNSIntegerType = 15, kCFNumberCGFloatType = 16, kCFNumberMaxType = 16 };
typedef CFIndex CFNumberType;
typedef const struct __CFNumber * CFNumberRef;
extern CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType theType, const void *valuePtr);
typedef const struct __CFAttributedString *CFAttributedStringRef;
typedef struct __CFAttributedString *CFMutableAttributedStringRef;
extern CFAttributedStringRef CFAttributedStringCreate(CFAllocatorRef alloc, CFStringRef str, CFDictionaryRef attributes) ;
extern CFMutableAttributedStringRef CFAttributedStringCreateMutableCopy(CFAllocatorRef alloc, CFIndex maxLength, CFAttributedStringRef aStr) ;
extern void CFAttributedStringSetAttribute(CFMutableAttributedStringRef aStr, CFRange range, CFStringRef attrName, CFTypeRef value) ;
typedef signed char BOOL;
typedef unsigned long NSUInteger;
@class NSString, Protocol;
extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2)));
typedef struct _NSZone NSZone;
@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator;
@protocol NSObject
- (BOOL)isEqual:(id)object;
- (id)retain;
- (oneway void)release;
- (id)autorelease;
- (Class)class;
@end @protocol NSCopying - (id)copyWithZone:(NSZone *)zone;
@end @protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone;
@end @protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder;
@end
@interface NSObject <NSObject> {}
+ (id)allocWithZone:(NSZone *)zone;
+ (id)alloc;
- (void)dealloc;
- (oneway void)release;
- (id)copy;
@end
@interface NSObject (NSCoderMethods)
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder;
@end
extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone);
typedef struct {
}
NSFastEnumerationState;
@protocol NSFastEnumeration - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len;
@end @class NSString, NSDictionary;
@interface NSValue : NSObject <NSCopying, NSCoding> - (void)getValue:(void *)value;
@end @interface NSNumber : NSValue - (char)charValue;
- (id)initWithInt:(int)value;
@end @class NSString;
@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>
- (NSUInteger)count;
@end
@interface NSArray (NSArrayCreation)
+ (id)array;
+ (id)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt;
@end
@interface NSAutoreleasePool : NSObject {
}
- (void)drain;
- (id)init;
@end extern NSString * const NSBundleDidLoadNotification;
typedef double NSTimeInterval;
@interface NSDate : NSObject <NSCopying, NSCoding> - (NSTimeInterval)timeIntervalSinceReferenceDate;
@end typedef unsigned short unichar;
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding> - (NSUInteger)length;
- ( const char *)UTF8String;
- (id)initWithUTF8String:(const char *)nullTerminatedCString;
+ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
@end @class NSString, NSURL, NSError;
@interface NSData : NSObject <NSCopying, NSMutableCopying, NSCoding> - (NSUInteger)length;
+ (id)dataWithBytesNoCopy:(void *)bytes length:(NSUInteger)length;
+ (id)dataWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b;
@end @class NSLocale, NSDate, NSCalendar, NSTimeZone, NSError, NSArray, NSMutableDictionary;
@interface NSDictionary : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration> - (NSUInteger)count;
@end @interface NSMutableDictionary : NSDictionary - (void)removeObjectForKey:(id)aKey;
- (void)setObject:(id)anObject forKey:(id)aKey;
@end @interface NSMutableDictionary (NSMutableDictionaryCreation) + (id)dictionaryWithCapacity:(NSUInteger)numItems;
@end typedef double CGFloat;
struct CGSize {
};
typedef struct CGSize CGSize;
struct CGRect {
};
typedef struct CGRect CGRect;
typedef mach_port_t io_object_t;
typedef char io_name_t[128];
typedef io_object_t io_iterator_t;
typedef io_object_t io_service_t;
typedef struct IONotificationPort * IONotificationPortRef;
typedef void (*IOServiceMatchingCallback)( void * refcon, io_iterator_t iterator );
io_service_t IOServiceGetMatchingService( mach_port_t masterPort, CFDictionaryRef matching );
kern_return_t IOServiceGetMatchingServices( mach_port_t masterPort, CFDictionaryRef matching, io_iterator_t * existing );
kern_return_t IOServiceAddNotification( mach_port_t masterPort, const io_name_t notificationType, CFDictionaryRef matching, mach_port_t wakePort, uintptr_t reference, io_iterator_t * notification ) __attribute__((deprecated));
kern_return_t IOServiceAddMatchingNotification( IONotificationPortRef notifyPort, const io_name_t notificationType, CFDictionaryRef matching, IOServiceMatchingCallback callback, void * refCon, io_iterator_t * notification );
CFMutableDictionaryRef IOServiceMatching( const char * name );
CFMutableDictionaryRef IOServiceNameMatching( const char * name );
CFMutableDictionaryRef IOBSDNameMatching( mach_port_t masterPort, uint32_t options, const char * bsdName );
CFMutableDictionaryRef IOOpenFirmwarePathMatching( mach_port_t masterPort, uint32_t options, const char * path );
CFMutableDictionaryRef IORegistryEntryIDMatching( uint64_t entryID );
typedef struct __DASession * DASessionRef;
extern DASessionRef DASessionCreate( CFAllocatorRef allocator );
typedef struct __DADisk * DADiskRef;
extern DADiskRef DADiskCreateFromBSDName( CFAllocatorRef allocator, DASessionRef session, const char * name );
extern DADiskRef DADiskCreateFromIOMedia( CFAllocatorRef allocator, DASessionRef session, io_service_t media );
extern CFDictionaryRef DADiskCopyDescription( DADiskRef disk );
extern DADiskRef DADiskCopyWholeDisk( DADiskRef disk );
@interface NSTask : NSObject - (id)init;
@end typedef struct CGColorSpace *CGColorSpaceRef;
typedef struct CGImage *CGImageRef;
typedef struct CGLayer *CGLayerRef;
@interface NSResponder : NSObject <NSCoding> {
}
@end @protocol NSAnimatablePropertyContainer - (id)animator;
@end extern NSString *NSAnimationTriggerOrderIn ;
@interface NSView : NSResponder <NSAnimatablePropertyContainer> {
}
@end @protocol NSValidatedUserInterfaceItem - (SEL)action;
@end @protocol NSUserInterfaceValidations - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem;
@end @class NSDate, NSDictionary, NSError, NSException, NSNotification;
@interface NSApplication : NSResponder <NSUserInterfaceValidations> {
}
@end enum {
NSTerminateCancel = 0, NSTerminateNow = 1, NSTerminateLater = 2 };
typedef NSUInteger NSApplicationTerminateReply;
@protocol NSApplicationDelegate <NSObject> @optional - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
@end @class NSAttributedString, NSEvent, NSFont, NSFormatter, NSImage, NSMenu, NSText, NSView, NSTextView;
@interface NSCell : NSObject <NSCopying, NSCoding> {
}
@end @class NSTextField, NSPanel, NSArray, NSWindow, NSImage, NSButton, NSError;
typedef struct {
}
CVTimeStamp;
@interface CIImage : NSObject <NSCoding, NSCopying> {
}
typedef int CIFormat;
@end enum {
kDAReturnSuccess = 0, kDAReturnError = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x01, kDAReturnBusy = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x02, kDAReturnBadArgument = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x03, kDAReturnExclusiveAccess = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x04, kDAReturnNoResources = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x05, kDAReturnNotFound = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x06, kDAReturnNotMounted = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x07, kDAReturnNotPermitted = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x08, kDAReturnNotPrivileged = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x09, kDAReturnNotReady = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x0A, kDAReturnNotWritable = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x0B, kDAReturnUnsupported = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x0C };
typedef mach_error_t DAReturn;
typedef const struct __DADissenter * DADissenterRef;
extern DADissenterRef DADissenterCreate( CFAllocatorRef allocator, DAReturn status, CFStringRef string );
@interface CIContext: NSObject {
}
- (CGImageRef)createCGImage:(CIImage *)im fromRect:(CGRect)r;
- (CGImageRef)createCGImage:(CIImage *)im fromRect:(CGRect)r format:(CIFormat)f colorSpace:(CGColorSpaceRef)cs;
- (CGLayerRef)createCGLayerWithSize:(CGSize)size info:(CFDictionaryRef)d;
@end extern NSString* const QCRendererEventKey;
@protocol QCCompositionRenderer - (NSDictionary*) attributes;
@end @interface QCRenderer : NSObject <QCCompositionRenderer> {
}
- (id) createSnapshotImageOfType:(NSString*)type;
@end extern NSString* const QCViewDidStartRenderingNotification;
@interface QCView : NSView <QCCompositionRenderer> {
}
- (id) createSnapshotImageOfType:(NSString*)type;
@end enum {
ICEXIFOrientation1 = 1, ICEXIFOrientation2 = 2, ICEXIFOrientation3 = 3, ICEXIFOrientation4 = 4, ICEXIFOrientation5 = 5, ICEXIFOrientation6 = 6, ICEXIFOrientation7 = 7, ICEXIFOrientation8 = 8, };
@class ICDevice;
@protocol ICDeviceDelegate <NSObject> @required - (void)didRemoveDevice:(ICDevice*)device;
@end extern NSString *const ICScannerStatusWarmingUp;
@class ICScannerDevice;
@protocol ICScannerDeviceDelegate <ICDeviceDelegate> @optional - (void)scannerDeviceDidBecomeAvailable:(ICScannerDevice*)scanner;
@end
CFTypeRef CFMakeCollectable(CFTypeRef cf) ;
static __inline__ __attribute__((always_inline)) id NSMakeCollectable(CFTypeRef
cf) {
return cf ? (id)CFMakeCollectable(cf) : ((void*)0);
}
//===----------------------------------------------------------------------===//
// Test cases.
//===----------------------------------------------------------------------===//
void f1() {
CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning
id x = [(id) A autorelease];
CFRelease((CFMutableArrayRef) x);
}
void f2() {
CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning{{leak}}
id x = [(id) A retain];
[x release];
[x release];
}
void f3() {
CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning{{leak}}
CFMakeCollectable(A);
CFRetain(A);
}
void f3b() {
CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning
CFMakeCollectable(A);
}
void f4() {
CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning{{leak}}
NSMakeCollectable(A);
CFRetain(A);
}
void f4b() {
CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning
NSMakeCollectable(A);
}
void f5() {
id x = [NSMakeCollectable(CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks)) autorelease]; // no-warning
}
void f5b() {
id x = [(id) CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks) autorelease]; // expected-warning{{leak}}
}
// Test return of non-owned objects in contexts where an owned object
// is expected.
@interface TestReturnNotOwnedWhenExpectedOwned
- (NSString*)newString;
- (CFMutableArrayRef)newArray;
@end
@implementation TestReturnNotOwnedWhenExpectedOwned
- (NSString*)newString {
NSString *s = [NSString stringWithUTF8String:"hello"]; // expected-warning{{Potential leak (when using garbage collection) of an object}}
CFRetain(s);
return s;
}
- (CFMutableArrayRef)newArray{
return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning
}
@end
//===----------------------------------------------------------------------===//
// <rdar://problem/6948053> False positive: object substitution during -init*
// methods warns about returning +0 when using -fobjc-gc-only
//===----------------------------------------------------------------------===//
@interface MyClassRdar6948053 : NSObject
- (id) init;
+ (id) shared;
@end
@implementation MyClassRdar6948053
+(id) shared {
return (id) 0;
}
- (id) init
{
Class myClass = [self class];
[self release];
return [[myClass shared] retain]; // no-warning
}
@end
//===----------------------------------------------------------------------===//
// <rdar://problem/7174400> 'ciContext createCGImage:outputImage fromRect:' returns a retained CF object (not GC'ed)//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
void rdar_7174400(QCView *view, QCRenderer *renderer, CIContext *context,
NSString *str, CIImage *img, CGRect rect,
CIFormat form, CGColorSpaceRef cs) {
[view createSnapshotImageOfType:str]; // no-warning
[renderer createSnapshotImageOfType:str]; // no-warning
[context createCGImage:img fromRect:rect]; // expected-warning{{leak}}
[context createCGImage:img fromRect:rect format:form colorSpace:cs]; // expected-warning{{leak}}
}
//===----------------------------------------------------------------------===//
// <rdar://problem/6250216> Warn against using -[NSAutoreleasePool release] in
// GC mode
//===----------------------------------------------------------------------===//
void rdar_6250216(void) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[pool release]; // expected-warning{{Use -drain instead of -release when using NSAutoreleasePool and garbage collection}}
}
//===----------------------------------------------------------------------===//
// <rdar://problem/7407273> Don't crash when analyzing messages sent to blocks
//===----------------------------------------------------------------------===//
@class RDar7407273;
typedef void (^RDar7407273Block)(RDar7407273 *operation);
void rdar7407273(RDar7407273Block b) {
[b copy];
}
//===----------------------------------------------------------------------===//
// Tests of ownership attributes.
//===----------------------------------------------------------------------===//
@interface TestOwnershipAttr : NSObject
- (NSString*) returnsAnOwnedString __attribute__((ns_returns_retained));
- (NSString*) returnsAnOwnedCFString __attribute__((cf_returns_retained));
@end
void test_attr_1(TestOwnershipAttr *X) {
NSString *str = [X returnsAnOwnedString]; // no-warning
}
void test_attr_1b(TestOwnershipAttr *X) {
NSString *str = [X returnsAnOwnedCFString]; // expected-warning{{leak}}
}
@interface MyClassTestCFAttr : NSObject {}
- (NSDate*) returnsCFRetained __attribute__((cf_returns_retained));
- (NSDate*) alsoReturnsRetained;
- (NSDate*) returnsNSRetained __attribute__((ns_returns_retained));
@end
__attribute__((cf_returns_retained))
CFDateRef returnsRetainedCFDate() {
return CFDateCreate(0, CFAbsoluteTimeGetCurrent());
}
@implementation MyClassTestCFAttr
- (NSDate*) returnsCFRetained {
return (NSDate*) returnsRetainedCFDate(); // No leak.
}
- (NSDate*) alsoReturnsRetained {
return (NSDate*) returnsRetainedCFDate(); // expected-warning{{leak}}
}
- (NSDate*) returnsNSRetained {
return (NSDate*) returnsRetainedCFDate(); // expected-warning{{leak}}
}
@end
#if __has_feature(attribute_ns_consumed)
#define NS_CONSUMED __attribute__((ns_consumed))
#endif
#if __has_feature(attribute_cf_consumed)
#define CF_CONSUMED __attribute__((cf_consumed))
#endif
void consumeAndStopTracking(id NS_CONSUMED obj, void (^callback)(void));
void CFConsumeAndStopTracking(CFTypeRef CF_CONSUMED obj, void (^callback)(void));
void testConsumeAndStopTracking() {
id retained = [@[] retain]; // +0, GC
consumeAndStopTracking(retained, ^{}); // no-warning
id doubleRetained = [[@[] retain] retain]; // +0, GC
consumeAndStopTracking(doubleRetained, ^{
[doubleRetained release];
}); // no-warning
id unretained = @[]; // +0
consumeAndStopTracking(unretained, ^{}); // no-warning, GC
}
void testCFConsumeAndStopTrackingMsg() {
id retained = [@[] retain]; // +0, GC
CFConsumeAndStopTracking((CFTypeRef)retained, ^{}); // expected-warning {{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
}
void testCFConsumeAndStopTracking() {
CFTypeRef retained = returnsRetainedCFDate(); // +1
CFConsumeAndStopTracking(retained, ^{}); // no-warning
CFTypeRef doubleRetained = CFRetain(returnsRetainedCFDate()); // +2
CFConsumeAndStopTracking(doubleRetained, ^{
CFRelease(doubleRetained);
}); // no-warning
id unretained = @[]; // +0
CFConsumeAndStopTracking((CFTypeRef)unretained, ^{}); // expected-warning {{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
}

View File

@ -1,75 +0,0 @@
// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -analyzer-store=region -fobjc-gc-only -analyzer-output=text -verify %s
// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -analyzer-store=region -fobjc-gc-only -analyzer-output=plist-multi-file %s -o %t.plist
// RUN: tail -n +11 %t.plist | diff -u -w - %S/Inputs/expected-plists/retain-release-path-notes-gc.m.plist
/***
This file is for testing the path-sensitive notes for retain/release errors.
Its goal is to have simple branch coverage of any path-based diagnostics,
not to actually check all possible retain/release errors.
This file is for notes that only appear in a GC-enabled analysis.
Non-specific and ref-count-only notes should go in retain-release-path-notes.m.
***/
@interface NSObject
+ (id)alloc;
- (id)init;
- (void)dealloc;
- (Class)class;
- (id)retain;
- (void)release;
- (void)autorelease;
@end
@interface Foo : NSObject
- (id)methodWithValue;
@property(retain) id propertyValue;
@end
typedef struct CFType *CFTypeRef;
CFTypeRef CFRetain(CFTypeRef);
void CFRelease(CFTypeRef);
id NSMakeCollectable(CFTypeRef);
CFTypeRef CFMakeCollectable(CFTypeRef);
CFTypeRef CFCreateSomething();
CFTypeRef CFGetSomething();
void creationViaCFCreate () {
CFTypeRef leaked = CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type CFTypeRef with a +1 retain count. Core Foundation objects are not automatically garbage collected}}
return; // expected-warning{{leak}} expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}}
}
void makeCollectable () {
CFTypeRef leaked = CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type CFTypeRef with a +1 retain count. Core Foundation objects are not automatically garbage collected}}
CFRetain(leaked); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
CFMakeCollectable(leaked); // expected-note{{In GC mode a call to 'CFMakeCollectable' decrements an object's retain count and registers the object with the garbage collector. An object must have a 0 retain count to be garbage collected. After this call its retain count is +1}}
NSMakeCollectable(leaked); // expected-note{{In GC mode a call to 'NSMakeCollectable' decrements an object's retain count and registers the object with the garbage collector. Since it now has a 0 retain count the object can be automatically collected by the garbage collector}}
CFRetain(leaked); // expected-note{{Reference count incremented. The object now has a +1 retain count. The object is not eligible for garbage collection until the retain count reaches 0 again}}
return; // expected-warning{{leak}} expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}}
}
void retainReleaseIgnored () {
id object = [[NSObject alloc] init]; // expected-note{{Method returns an instance of NSObject with a +0 retain count}}
[object retain]; // expected-note{{In GC mode the 'retain' message has no effect}}
[object release]; // expected-note{{In GC mode the 'release' message has no effect}}
[object autorelease]; // expected-note{{In GC mode an 'autorelease' has no effect}}
CFRelease((CFTypeRef)object); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
}
@implementation Foo (FundamentalRuleUnderGC)
- (id)getViolation {
id object = (id) CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type CFTypeRef with a +1 retain count. Core Foundation objects are not automatically garbage collected}}
return object; // expected-warning{{leak}} expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'object' and returned from method 'getViolation' 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}}
}
- (id)copyViolation {
id object = (id) CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type CFTypeRef with a +1 retain count. Core Foundation objects are not automatically garbage collected}}
return object; // expected-warning{{leak}} expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'object' and returned from method 'copyViolation' 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}}
}
@end

View File

@ -36,9 +36,6 @@ CFTypeRef CFRetain(CFTypeRef);
void CFRelease(CFTypeRef); void CFRelease(CFTypeRef);
CFTypeRef CFAutorelease(CFTypeRef __attribute__((cf_consumed))); CFTypeRef CFAutorelease(CFTypeRef __attribute__((cf_consumed)));
id NSMakeCollectable(CFTypeRef);
CFTypeRef CFMakeCollectable(CFTypeRef);
CFTypeRef CFCreateSomething(); CFTypeRef CFCreateSomething();
CFTypeRef CFGetSomething(); CFTypeRef CFGetSomething();
@ -98,13 +95,6 @@ void autoreleaseUnowned (Foo *foo) {
return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased but has a +0 retain count}} return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased but has a +0 retain count}}
} }
void makeCollectableIgnored () {
CFTypeRef leaked = CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type CFTypeRef with a +1 retain count}}
CFMakeCollectable(leaked); // expected-note{{When GC is not enabled a call to 'CFMakeCollectable' has no effect on its argument}}
NSMakeCollectable(leaked); // expected-note{{When GC is not enabled a call to 'NSMakeCollectable' has no effect on its argument}}
return; // expected-warning{{leak}} expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}}
}
CFTypeRef CFCopyRuleViolation () { CFTypeRef CFCopyRuleViolation () {
CFTypeRef object = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object of type CFTypeRef with a +0 retain count}} CFTypeRef object = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object of type CFTypeRef with a +0 retain count}}
return object; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} return object; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}

View File

@ -318,9 +318,6 @@ extern CGColorSpaceRef CGColorSpaceCreateDeviceRGB(void);
+ (id)array; + (id)array;
@end @end
// This is how NSMakeCollectable is declared in the OS X 10.8 headers.
id NSMakeCollectable(CFTypeRef __attribute__((cf_consumed))) __attribute__((ns_returns_retained));
typedef const struct __CFUUID * CFUUIDRef; typedef const struct __CFUUID * CFUUIDRef;
extern extern
@ -2076,17 +2073,6 @@ void rdar11400885(int y)
} }
} }
id makeCollectableNonLeak() {
extern CFTypeRef CFCreateSomething();
CFTypeRef object = CFCreateSomething(); // +1
CFRetain(object); // +2
id objCObject = NSMakeCollectable(object); // +2
[objCObject release]; // +1
return [objCObject autorelease]; // +0
}
void consumeAndStopTracking(id NS_CONSUMED obj, void (^callback)(void)); void consumeAndStopTracking(id NS_CONSUMED obj, void (^callback)(void));
void CFConsumeAndStopTracking(CFTypeRef CF_CONSUMED obj, void (^callback)(void)); void CFConsumeAndStopTracking(CFTypeRef CF_CONSUMED obj, void (^callback)(void));