r348638 - [analyzer] Move out tracking retain count for OSObjects into a separate checker
George Karpenkov via cfe-commits
cfe-commits at lists.llvm.org
Fri Dec 7 12:21:51 PST 2018
Author: george.karpenkov
Date: Fri Dec 7 12:21:51 2018
New Revision: 348638
URL: http://llvm.org/viewvc/llvm-project?rev=348638&view=rev
Log:
[analyzer] Move out tracking retain count for OSObjects into a separate checker
Allow enabling and disabling tracking of ObjC/CF objects
separately from tracking of OS objects.
Differential Revision: https://reviews.llvm.org/D55400
Added:
cfe/trunk/test/Analysis/test-separate-retaincount.cpp
Modified:
cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td
cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h
cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp
cfe/trunk/test/Analysis/osobject-retain-release.cpp
Modified: cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td?rev=348638&r1=348637&r2=348638&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td Fri Dec 7 12:21:51 2018
@@ -520,6 +520,9 @@ def MacOSKeychainAPIChecker : Checker<"S
def ObjCPropertyChecker : Checker<"ObjCProperty">,
HelpText<"Check for proper uses of Objective-C properties">;
+def OSObjectRetainCountChecker : Checker<"OSObjectRetainCount">,
+ HelpText<"Check for leaks and improper reference count management for OSObject">;
+
} // end "osx"
let ParentPackage = Cocoa in {
Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h?rev=348638&r1=348637&r2=348638&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h Fri Dec 7 12:21:51 2018
@@ -469,6 +469,8 @@ public:
}
};
+class RetainSummaryTemplate;
+
class RetainSummaryManager {
typedef llvm::DenseMap<const FunctionDecl*, const RetainSummary *>
FuncSummariesTy;
@@ -483,7 +485,10 @@ class RetainSummaryManager {
/// Records whether or not the analyzed code runs in ARC mode.
const bool ARCEnabled;
- /// Track sublcasses of OSObject
+ /// Track Objective-C and CoreFoundation objects.
+ const bool TrackObjCAndCFObjects;
+
+ /// Track sublcasses of OSObject.
const bool TrackOSObjects;
/// FuncSummaries - A map from FunctionDecls to summaries.
@@ -626,13 +631,36 @@ class RetainSummaryManager {
const RetainSummary * generateSummary(const FunctionDecl *FD,
bool &AllowAnnotations);
+ /// Return a summary for OSObject, or nullptr if not found.
+ const RetainSummary *getSummaryForOSObject(const FunctionDecl *FD,
+ StringRef FName, QualType RetTy);
+
+ /// Return a summary for Objective-C or CF object, or nullptr if not found.
+ const RetainSummary *getSummaryForObjCOrCFObject(
+ const FunctionDecl *FD,
+ StringRef FName,
+ QualType RetTy,
+ const FunctionType *FT,
+ bool &AllowAnnotations);
+
+ /// Apply the annotation of {@code pd} in function {@code FD}
+ /// to the resulting summary stored in out-parameter {@code Template}.
+ /// \return whether an annotation was applied.
+ bool applyFunctionParamAnnotationEffect(const ParmVarDecl *pd,
+ unsigned parm_idx,
+ const FunctionDecl *FD,
+ ArgEffects::Factory &AF,
+ RetainSummaryTemplate &Template);
+
public:
RetainSummaryManager(ASTContext &ctx,
bool usesARC,
- bool trackOSObject)
+ bool trackObjCAndCFObjects,
+ bool trackOSObjects)
: Ctx(ctx),
ARCEnabled(usesARC),
- TrackOSObjects(trackOSObject),
+ TrackObjCAndCFObjects(trackObjCAndCFObjects),
+ TrackOSObjects(trackOSObjects),
AF(BPAlloc), ScratchArgs(AF.getEmptyMap()),
ObjCAllocRetE(usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC)
: RetEffect::MakeOwned(RetEffect::ObjC)),
@@ -709,6 +737,7 @@ public:
void updateSummaryFromAnnotations(const RetainSummary *&Summ,
const FunctionDecl *FD);
+
void updateSummaryForCall(const RetainSummary *&Summ,
const CallEvent &Call);
@@ -716,9 +745,21 @@ public:
RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
+ /// \return True if the declaration has an attribute {@code T},
+ /// AND we are tracking that attribute. False otherwise.
+ template <class T>
+ bool hasEnabledAttr(const Decl *D) {
+ return isAttrEnabled<T>() && D->hasAttr<T>();
+ }
+
+ /// Check whether we are tracking properties specified by the attributes.
+ template <class T>
+ bool isAttrEnabled();
+
friend class RetainSummaryTemplate;
};
+
// 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
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp?rev=348638&r1=348637&r2=348638&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp Fri Dec 7 12:21:51 2018
@@ -1504,9 +1504,10 @@ void RetainCountChecker::printState(raw_
void ento::registerRetainCountChecker(CheckerManager &Mgr) {
auto *Chk = Mgr.registerChecker<RetainCountChecker>();
+ Chk->TrackObjCAndCFObjects = true;
+}
- AnalyzerOptions &Options = Mgr.getAnalyzerOptions();
-
- Chk->ShouldCheckOSObjectRetainCount = Options.getCheckerBooleanOption(
- "CheckOSObject", true, Chk);
+void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) {
+ auto *Chk = Mgr.registerChecker<RetainCountChecker>();
+ Chk->TrackOSObjects = true;
}
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h?rev=348638&r1=348637&r2=348638&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h Fri Dec 7 12:21:51 2018
@@ -92,7 +92,7 @@ private:
/// See the RefVal::Kind enum for possible values.
unsigned RawKind : 5;
- /// The kind of object being tracked (CF or ObjC), if known.
+ /// The kind of object being tracked (CF or ObjC or OSObject), if known.
///
/// See the RetEffect::ObjKind enum for possible values.
unsigned RawObjectKind : 3;
@@ -268,10 +268,12 @@ class RetainCountChecker
mutable bool ShouldResetSummaryLog;
public:
- /// Optional setting to indicate if leak reports should include
- /// the allocation line.
- bool IncludeAllocationLine;
- bool ShouldCheckOSObjectRetainCount;
+
+ /// Track Objective-C and CoreFoundation objects.
+ bool TrackObjCAndCFObjects = false;
+
+ /// Track sublcasses of OSObject.
+ bool TrackOSObjects = false;
RetainCountChecker() : ShouldResetSummaryLog(false) {}
@@ -290,7 +292,7 @@ public:
bool ARCEnabled = (bool)Ctx.getLangOpts().ObjCAutoRefCount;
if (!Summaries) {
Summaries.reset(new RetainSummaryManager(
- Ctx, ARCEnabled, ShouldCheckOSObjectRetainCount));
+ Ctx, ARCEnabled, TrackObjCAndCFObjects, TrackOSObjects));
} else {
assert(Summaries->isARCEnabled() == ARCEnabled);
}
Modified: cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp?rev=348638&r1=348637&r2=348638&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp Fri Dec 7 12:21:51 2018
@@ -24,6 +24,31 @@
using namespace clang;
using namespace ento;
+template <class T>
+constexpr static bool isOneOf() {
+ return false;
+}
+
+/// Helper function to check whether the class is one of the
+/// rest of varargs.
+template <class T, class P, class... ToCompare>
+constexpr static bool isOneOf() {
+ return std::is_same<T, P>::value || isOneOf<T, ToCompare...>();
+}
+
+template <class T> bool RetainSummaryManager::isAttrEnabled() {
+ if (isOneOf<T, CFConsumedAttr, CFReturnsRetainedAttr,
+ CFReturnsNotRetainedAttr, NSConsumedAttr, NSConsumesSelfAttr,
+ NSReturnsAutoreleasedAttr, NSReturnsRetainedAttr,
+ NSReturnsNotRetainedAttr>()) {
+ return TrackObjCAndCFObjects;
+ } else if (isOneOf<T, OSConsumedAttr, OSConsumesThisAttr,
+ OSReturnsNotRetainedAttr, OSReturnsRetainedAttr>()) {
+ return TrackOSObjects;
+ }
+ llvm_unreachable("Unexpected attribute passed");
+}
+
ArgEffects RetainSummaryManager::getArgEffects() {
ArgEffects AE = ScratchArgs;
ScratchArgs = AF.getEmptyMap();
@@ -116,30 +141,60 @@ static bool isOSObjectRelated(const CXXM
}
const RetainSummary *
-RetainSummaryManager::generateSummary(const FunctionDecl *FD,
- bool &AllowAnnotations) {
- // We generate "stop" summaries for implicitly defined functions.
- if (FD->isImplicit()) {
- return getPersistentStopSummary();
+RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD,
+ StringRef FName, QualType RetTy) {
+ if (RetTy->isPointerType()) {
+ const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl();
+ if (PD && isOSObjectSubclass(PD)) {
+ if (const IdentifierInfo *II = FD->getIdentifier()) {
+ if (isOSObjectDynamicCast(II->getName()))
+ return getDefaultSummary();
+
+ // All objects returned with functions *not* starting with
+ // get, or iterators, are returned at +1.
+ if ((!II->getName().startswith("get") &&
+ !II->getName().startswith("Get")) ||
+ isOSIteratorSubclass(PD)) {
+ return getOSSummaryCreateRule(FD);
+ } else {
+ return getOSSummaryGetRule(FD);
+ }
+ }
+ }
}
- const IdentifierInfo *II = FD->getIdentifier();
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ const CXXRecordDecl *Parent = MD->getParent();
+ if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) {
+ if (FName == "release")
+ return getOSSummaryReleaseRule(FD);
- StringRef FName = II ? II->getName() : "";
+ if (FName == "retain")
+ return getOSSummaryRetainRule(FD);
- // Strip away preceding '_'. Doing this here will effect all the checks
- // down below.
- FName = FName.substr(FName.find_first_not_of('_'));
+ if (FName == "free")
+ return getOSSummaryFreeRule(FD);
+
+ if (MD->getOverloadedOperator() == OO_New)
+ return getOSSummaryCreateRule(MD);
+ }
+ }
+
+ return nullptr;
+}
+
+const RetainSummary *RetainSummaryManager::getSummaryForObjCOrCFObject(
+ const FunctionDecl *FD,
+ StringRef FName,
+ QualType RetTy,
+ const FunctionType *FT,
+ bool &AllowAnnotations) {
- // Inspect the result type. Strip away any typedefs.
- const auto *FT = FD->getType()->getAs<FunctionType>();
- QualType RetTy = FT->getReturnType();
std::string RetTyName = RetTy.getAsString();
// FIXME: This should all be refactored into a chain of "summary lookup"
// filters.
assert(ScratchArgs.isEmpty());
-
if (FName == "pthread_create" || FName == "pthread_setspecific") {
// Part of: <rdar://problem/7299394> and <rdar://problem/11282706>.
// This will be addressed better with IPA.
@@ -230,30 +285,11 @@ RetainSummaryManager::generateSummary(co
if (RetTy->isPointerType()) {
- const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl();
- if (TrackOSObjects && PD && isOSObjectSubclass(PD)) {
- if (const IdentifierInfo *II = FD->getIdentifier()) {
-
- if (isOSObjectDynamicCast(II->getName()))
- return getDefaultSummary();
-
- // All objects returned with functions *not* starting with
- // get, or iterators, are returned at +1.
- if ((!II->getName().startswith("get") &&
- !II->getName().startswith("Get")) ||
- isOSIteratorSubclass(PD)) {
- return getOSSummaryCreateRule(FD);
- } else {
- return getOSSummaryGetRule(FD);
- }
- }
- }
-
// For CoreFoundation ('CF') types.
if (cocoa::isRefType(RetTy, "CF", FName)) {
if (isRetain(FD, FName)) {
- // CFRetain isn't supposed to be annotated. However, this may as well
- // be a user-made "safe" CFRetain function that is incorrectly
+ // CFRetain isn't supposed to be annotated. However, this may as
+ // well be a user-made "safe" CFRetain function that is incorrectly
// annotated as cf_returns_retained due to lack of better options.
// We want to ignore such annotation.
AllowAnnotations = false;
@@ -294,27 +330,9 @@ RetainSummaryManager::generateSummary(co
}
}
- if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
- const CXXRecordDecl *Parent = MD->getParent();
- if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) {
- if (FName == "release")
- return getOSSummaryReleaseRule(FD);
-
- if (FName == "retain")
- return getOSSummaryRetainRule(FD);
-
- if (FName == "free")
- return getOSSummaryFreeRule(FD);
-
- if (MD->getOverloadedOperator() == OO_New)
- return getOSSummaryCreateRule(MD);
- }
- }
-
// Check for release functions, the only kind of functions that we care
// about that don't return a pointer type.
- if (FName.size() >= 2 && FName[0] == 'C' &&
- (FName[1] == 'F' || FName[1] == 'G')) {
+ if (FName.startswith("CG") || FName.startswith("CF")) {
// Test for 'CGCF'.
FName = FName.substr(FName.startswith("CGCF") ? 4 : 2);
@@ -349,11 +367,41 @@ RetainSummaryManager::generateSummary(co
}
}
- if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ return nullptr;
+}
+
+const RetainSummary *
+RetainSummaryManager::generateSummary(const FunctionDecl *FD,
+ bool &AllowAnnotations) {
+ // We generate "stop" summaries for implicitly defined functions.
+ if (FD->isImplicit())
+ return getPersistentStopSummary();
+
+ const IdentifierInfo *II = FD->getIdentifier();
+
+ StringRef FName = II ? II->getName() : "";
+
+ // Strip away preceding '_'. Doing this here will effect all the checks
+ // down below.
+ FName = FName.substr(FName.find_first_not_of('_'));
+
+ // Inspect the result type. Strip away any typedefs.
+ const auto *FT = FD->getType()->getAs<FunctionType>();
+ QualType RetTy = FT->getReturnType();
+
+ if (TrackOSObjects)
+ if (const RetainSummary *S = getSummaryForOSObject(FD, FName, RetTy))
+ return S;
+
+ if (TrackObjCAndCFObjects)
+ if (const RetainSummary *S =
+ getSummaryForObjCOrCFObject(FD, FName, RetTy, FT, AllowAnnotations))
+ return S;
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
if (!(TrackOSObjects && isOSObjectRelated(MD)))
return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking,
DoNothing);
- }
return getDefaultSummary();
}
@@ -658,7 +706,7 @@ RetainSummaryManager::getCFSummaryGetRul
Optional<RetEffect>
RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy,
const Decl *D) {
- if (cocoa::isCocoaObjectRef(RetTy)) {
+ if (TrackObjCAndCFObjects && cocoa::isCocoaObjectRef(RetTy)) {
if (D->hasAttr<NSReturnsRetainedAttr>())
return ObjCAllocRetE;
@@ -670,17 +718,17 @@ RetainSummaryManager::getRetEffectFromAn
return None;
}
- if (D->hasAttr<CFReturnsRetainedAttr>()) {
+ if (hasEnabledAttr<CFReturnsRetainedAttr>(D)) {
return RetEffect::MakeOwned(RetEffect::CF);
- } else if (D->hasAttr<OSReturnsRetainedAttr>()) {
+ } else if (hasEnabledAttr<OSReturnsRetainedAttr>(D)) {
return RetEffect::MakeOwned(RetEffect::OS);
} else if (hasRCAnnotation(D, "rc_ownership_returns_retained")) {
return RetEffect::MakeOwned(RetEffect::Generalized);
}
- if (D->hasAttr<CFReturnsNotRetainedAttr>()) {
+ if (hasEnabledAttr<CFReturnsNotRetainedAttr>(D)) {
return RetEffect::MakeNotOwned(RetEffect::CF);
- } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {
+ } else if (hasEnabledAttr<OSReturnsNotRetainedAttr>(D)) {
return RetEffect::MakeNotOwned(RetEffect::OS);
} else if (hasRCAnnotation(D, "rc_ownership_returns_not_retained")) {
return RetEffect::MakeNotOwned(RetEffect::Generalized);
@@ -694,22 +742,20 @@ RetainSummaryManager::getRetEffectFromAn
return None;
}
-/// Apply the annotation of {@code pd} in function {@code FD}
-/// to the resulting summary stored in out-parameter {@code Template}.
-/// Return whether an annotation was applied.
-bool applyFunctionParamAnnotationEffect(const ParmVarDecl *pd,
+bool RetainSummaryManager::applyFunctionParamAnnotationEffect(const ParmVarDecl *pd,
unsigned parm_idx,
const FunctionDecl *FD,
ArgEffects::Factory &AF,
RetainSummaryTemplate &Template) {
- if (pd->hasAttr<NSConsumedAttr>()) {
+ if (hasEnabledAttr<NSConsumedAttr>(pd)) {
Template->addArg(AF, parm_idx, DecRefMsg);
return true;
- } else if (pd->hasAttr<CFConsumedAttr>() || pd->hasAttr<OSConsumedAttr>() ||
+ } else if (hasEnabledAttr<CFConsumedAttr>(pd) ||
+ hasEnabledAttr<OSConsumedAttr>(pd) ||
hasRCAnnotation(pd, "rc_ownership_consumed")) {
Template->addArg(AF, parm_idx, DecRef);
return true;
- } else if (pd->hasAttr<CFReturnsRetainedAttr>() ||
+ } else if (hasEnabledAttr<CFReturnsRetainedAttr>(pd) ||
hasRCAnnotation(pd, "rc_ownership_returns_retained")) {
QualType PointeeTy = pd->getType()->getPointeeType();
if (!PointeeTy.isNull()) {
@@ -718,7 +764,7 @@ bool applyFunctionParamAnnotationEffect(
return true;
}
}
- } else if (pd->hasAttr<CFReturnsNotRetainedAttr>()) {
+ } else if (hasEnabledAttr<CFReturnsNotRetainedAttr>(pd)) {
QualType PointeeTy = pd->getType()->getPointeeType();
if (!PointeeTy.isNull()) {
if (coreFoundation::isCFObjectRef(PointeeTy)) {
@@ -760,7 +806,7 @@ RetainSummaryManager::updateSummaryFromA
if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, FD))
Template->setRetEffect(*RetE);
- if (FD->hasAttr<OSConsumesThisAttr>())
+ if (hasEnabledAttr<OSConsumesThisAttr>(FD))
Template->setThisEffect(DecRef);
}
@@ -779,8 +825,7 @@ RetainSummaryManager::updateSummaryFromA
// Effects on the parameters.
unsigned parm_idx = 0;
- for (ObjCMethodDecl::param_const_iterator
- pi=MD->param_begin(), pe=MD->param_end();
+ for (auto pi=MD->param_begin(), pe=MD->param_end();
pi != pe; ++pi, ++parm_idx) {
const ParmVarDecl *pd = *pi;
if (pd->hasAttr<NSConsumedAttr>()) {
@@ -933,6 +978,10 @@ RetainSummaryManager::getMethodSummary(S
const ObjCMethodDecl *MD, QualType RetTy,
ObjCMethodSummariesTy &CachedSummaries) {
+ // Objective-C method summaries are only applicable to ObjC and CF objects.
+ if (!TrackObjCAndCFObjects)
+ return getDefaultSummary();
+
// Look up a summary in our summary cache.
const RetainSummary *Summ = CachedSummaries.find(ID, S);
@@ -1043,7 +1092,9 @@ void RetainSummaryManager::InitializeMet
CallEffects CallEffects::getEffect(const ObjCMethodDecl *MD) {
ASTContext &Ctx = MD->getASTContext();
LangOptions L = Ctx.getLangOpts();
- RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, /*TrackOSObjects=*/false);
+ RetainSummaryManager M(Ctx, L.ObjCAutoRefCount,
+ /*TrackNSAndCFObjects=*/true,
+ /*TrackOSObjects=*/false);
const RetainSummary *S = M.getMethodSummary(MD);
CallEffects CE(S->getRetEffect());
CE.Receiver = S->getReceiverEffect();
@@ -1057,7 +1108,9 @@ CallEffects CallEffects::getEffect(const
CallEffects CallEffects::getEffect(const FunctionDecl *FD) {
ASTContext &Ctx = FD->getASTContext();
LangOptions L = Ctx.getLangOpts();
- RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, /*TrackOSObjects=*/false);
+ RetainSummaryManager M(Ctx, L.ObjCAutoRefCount,
+ /*TrackNSAndCFObjects=*/true,
+ /*TrackOSObjects=*/false);
const RetainSummary *S = M.getFunctionSummary(FD);
CallEffects CE(S->getRetEffect());
unsigned N = FD->param_size();
Modified: cfe/trunk/test/Analysis/osobject-retain-release.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/osobject-retain-release.cpp?rev=348638&r1=348637&r2=348638&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/osobject-retain-release.cpp (original)
+++ cfe/trunk/test/Analysis/osobject-retain-release.cpp Fri Dec 7 12:21:51 2018
@@ -1,4 +1,4 @@
-// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount -analyzer-output=text -verify %s
+// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core,osx -analyzer-output=text -verify %s
struct OSMetaClass;
Added: cfe/trunk/test/Analysis/test-separate-retaincount.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/test-separate-retaincount.cpp?rev=348638&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/test-separate-retaincount.cpp (added)
+++ cfe/trunk/test/Analysis/test-separate-retaincount.cpp Fri Dec 7 12:21:51 2018
@@ -0,0 +1,37 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-disable-checker osx.cocoa.RetainCount -DNO_CF_OBJECT -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-disable-checker osx.OSObjectRetainCount -DNO_OS_OBJECT -verify %s
+
+typedef const void * CFTypeRef;
+extern CFTypeRef CFRetain(CFTypeRef cf);
+extern void CFRelease(CFTypeRef cf);
+
+#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
+extern CFTypeRef CFCreate() CF_RETURNS_RETAINED;
+
+using size_t = decltype(sizeof(int));
+
+struct OSObject {
+ virtual void retain();
+ virtual void release();
+
+ static void * operator new(size_t size);
+ virtual ~OSObject(){}
+};
+
+void cf_overrelease() {
+ CFTypeRef cf = CFCreate();
+ CFRelease(cf);
+ CFRelease(cf);
+#ifndef NO_CF_OBJECT
+ // expected-warning at -2{{Reference-counted object is used after it is released}}
+#endif
+}
+
+void osobject_overrelease() {
+ OSObject *o = new OSObject;
+ o->release();
+ o->release();
+#ifndef NO_OS_OBJECT
+ // expected-warning at -2{{Reference-counted object is used after it is released}}
+#endif
+}
More information about the cfe-commits
mailing list