diff options
| author | Devin Coughlin <dcoughlin@apple.com> | 2016-03-25 21:18:22 +0000 |
|---|---|---|
| committer | Devin Coughlin <dcoughlin@apple.com> | 2016-03-25 21:18:22 +0000 |
| commit | b8076292abcfb10d87f3b107dd871b7327d18534 (patch) | |
| tree | 3ae5c02e4cdbaa6c32c7c2594cfd8e67a1419274 /clang/lib/StaticAnalyzer/Checkers | |
| parent | 36c53fe147378109ddebed3d3f62fafcccca105a (diff) | |
| download | bcm5719-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.cpp | 66 |
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. |

