summaryrefslogtreecommitdiffstats
path: root/clang/lib/StaticAnalyzer/Checkers
diff options
context:
space:
mode:
authorDevin Coughlin <dcoughlin@apple.com>2016-03-25 21:18:22 +0000
committerDevin Coughlin <dcoughlin@apple.com>2016-03-25 21:18:22 +0000
commitb8076292abcfb10d87f3b107dd871b7327d18534 (patch)
tree3ae5c02e4cdbaa6c32c7c2594cfd8e67a1419274 /clang/lib/StaticAnalyzer/Checkers
parent36c53fe147378109ddebed3d3f62fafcccca105a (diff)
downloadbcm5719-llvm-b8076292abcfb10d87f3b107dd871b7327d18534.tar.gz
bcm5719-llvm-b8076292abcfb10d87f3b107dd871b7327d18534.zip
[analyzer] Add CIFIlter modeling to DeallocChecker.
The -dealloc method in CIFilter is highly unusual in that it will release instance variables belonging to its *subclasses* if the variable name starts with "input" or backs a property whose name starts with "input". Subclasses should not release these ivars in their own -dealloc method -- doing so could result in an over release. Before this commit, the DeallocChecker would warn about missing releases for such "input" properties -- which could cause users of the analyzer to add over releases to silence the warning. To avoid this, DeallocChecker now treats CIFilter "input-prefixed" ivars as MustNotReleaseDirectly and so will not require a release. Further, it will now warn when such an ivar is directly released in -dealloc. rdar://problem/25364901 llvm-svn: 264463
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp66
1 files changed, 56 insertions, 10 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
index 84856bd6dcd..bee9deadd07 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -98,7 +98,8 @@ class ObjCDeallocChecker
check::PointerEscape,
check::PreStmt<ReturnStmt>> {
- mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *Block_releaseII;
+ mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *Block_releaseII,
+ *CIFilterII;
mutable Selector DeallocSel, ReleaseSel;
std::unique_ptr<BugType> MissingReleaseBugType;
@@ -169,6 +170,8 @@ private:
void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;
bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const;
+
+ bool isReleasedByCIFilterDealloc(const ObjCPropertyImplDecl *PropImpl) const;
};
} // End anonymous namespace.
@@ -688,19 +691,27 @@ bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue,
assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak ||
(PropDecl->getSetterKind() == ObjCPropertyDecl::Assign &&
- !PropDecl->isReadOnly()));
+ !PropDecl->isReadOnly()) ||
+ isReleasedByCIFilterDealloc(PropImpl)
+ );
const ObjCImplDecl *Container = getContainingObjCImpl(C.getLocationContext());
OS << "The '" << *PropImpl->getPropertyIvarDecl()
- << "' ivar in '" << *Container
- << "' was synthesized for ";
+ << "' ivar in '" << *Container;
- if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak)
- OS << "a weak";
- else
- OS << "an assign, readwrite";
- OS << " property but was released in 'dealloc'";
+ if (isReleasedByCIFilterDealloc(PropImpl)) {
+ OS << "' will be released by '-[CIFilter dealloc]' but also released here";
+ } else {
+ OS << "' was synthesized for ";
+
+ if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak)
+ OS << "a weak";
+ else
+ OS << "an assign, readwrite";
+
+ OS << " property but was released in 'dealloc'";
+ }
std::unique_ptr<BugReport> BR(
new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode));
@@ -751,7 +762,7 @@ bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue,
ObjCDeallocChecker::
ObjCDeallocChecker()
- : NSObjectII(nullptr), SenTestCaseII(nullptr) {
+ : NSObjectII(nullptr), SenTestCaseII(nullptr), CIFilterII(nullptr) {
MissingReleaseBugType.reset(
new BugType(this, "Missing ivar release (leak)",
@@ -774,6 +785,7 @@ void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
NSObjectII = &Ctx.Idents.get("NSObject");
SenTestCaseII = &Ctx.Idents.get("SenTestCase");
Block_releaseII = &Ctx.Idents.get("_Block_release");
+ CIFilterII = &Ctx.Idents.get("CIFilter");
IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc");
IdentifierInfo *ReleaseII = &Ctx.Idents.get("release");
@@ -894,6 +906,9 @@ ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement(
// the value in their instance variables must be released in -dealloc.
case ObjCPropertyDecl::Retain:
case ObjCPropertyDecl::Copy:
+ if (isReleasedByCIFilterDealloc(PropImpl))
+ return ReleaseRequirement::MustNotReleaseDirectly;
+
return ReleaseRequirement::MustRelease;
case ObjCPropertyDecl::Weak:
@@ -1019,6 +1034,37 @@ bool ObjCDeallocChecker::classHasSeparateTeardown(
return true;
}
+/// The -dealloc method in CIFilter highly unusual in that is will release
+/// instance variables belonging to its *subclasses* if the variable name
+/// starts with "input" or backs a property whose name starts with "input".
+/// Subclasses should not release these ivars in their own -dealloc method --
+/// doing so could result in an over release.
+///
+/// This method returns true if the property will be released by
+/// -[CIFilter dealloc].
+bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
+ const ObjCPropertyImplDecl *PropImpl) const {
+ assert(PropImpl->getPropertyIvarDecl());
+ StringRef PropName = PropImpl->getPropertyDecl()->getName();
+ StringRef IvarName = PropImpl->getPropertyIvarDecl()->getName();
+
+ const char *ReleasePrefix = "input";
+ if (!(PropName.startswith(ReleasePrefix) ||
+ IvarName.startswith(ReleasePrefix))) {
+ return false;
+ }
+
+ const ObjCInterfaceDecl *ID =
+ PropImpl->getPropertyIvarDecl()->getContainingInterface();
+ for ( ; ID ; ID = ID->getSuperClass()) {
+ IdentifierInfo *II = ID->getIdentifier();
+ if (II == CIFilterII)
+ return true;
+ }
+
+ return false;
+}
+
void ento::registerObjCDeallocChecker(CheckerManager &Mgr) {
const LangOptions &LangOpts = Mgr.getLangOpts();
// These checker only makes sense under MRR.
OpenPOWER on IntegriCloud