diff options
author | Ted Kremenek <kremenek@apple.com> | 2009-11-20 05:27:05 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2009-11-20 05:27:05 +0000 |
commit | 50e837b3cbc9315b6808daabb96c5c7cccf11ea7 (patch) | |
tree | df26ed438366eed2b5326fcc636d0e168285780f | |
parent | b221e4fb46f6e35b0721399ed2734daadbcc1f00 (diff) |
Add simple static analyzer checker to check for sending 'release', 'retain', etc. directly to a class. Fixes <rdar://problem/7252064>.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89449 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/Analysis/BasicObjCFoundationChecks.cpp | 61 | ||||
-rw-r--r-- | test/Analysis/retain-release.m | 13 |
2 files changed, 74 insertions, 0 deletions
diff --git a/lib/Analysis/BasicObjCFoundationChecks.cpp b/lib/Analysis/BasicObjCFoundationChecks.cpp index c2ecfa1f41..dd50fe0849 100644 --- a/lib/Analysis/BasicObjCFoundationChecks.cpp +++ b/lib/Analysis/BasicObjCFoundationChecks.cpp @@ -22,6 +22,7 @@ #include "clang/Analysis/PathSensitive/BugReporter.h" #include "clang/Analysis/PathSensitive/MemRegion.h" #include "clang/Analysis/PathDiagnostic.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" #include "clang/Analysis/LocalCheckers.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" @@ -522,6 +523,65 @@ clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) { } //===----------------------------------------------------------------------===// +// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. +//===----------------------------------------------------------------------===// + +namespace { +class VISIBILITY_HIDDEN ClassReleaseChecker : + public CheckerVisitor<ClassReleaseChecker> { + Selector releaseS; + Selector retainS; + Selector autoreleaseS; + Selector drainS; + BugType *BT; +public: + ClassReleaseChecker(ASTContext &Ctx) + : releaseS(GetNullarySelector("release", Ctx)), + retainS(GetNullarySelector("retain", Ctx)), + autoreleaseS(GetNullarySelector("autorelease", Ctx)), + drainS(GetNullarySelector("drain", Ctx)), + BT(0) {} + + static void *getTag() { static int x = 0; return &x; } + + void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); +}; +} + +void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C, + const ObjCMessageExpr *ME) { + + const IdentifierInfo *ClsName = ME->getClassName(); + if (!ClsName) + return; + + Selector S = ME->getSelector(); + if (!(S == releaseS || S == retainS || S == autoreleaseS | S == drainS)) + return; + + if (!BT) + BT = new APIMisuse("message incorrectly sent to class instead of class " + "instance"); + + ExplodedNode *N = C.GenerateNode(ME, C.getState(), false); + if (!N) + return; + + C.addTransition(N); + + llvm::SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + + os << "The '" << S.getAsString() << "' message should be sent to instances " + "of class '" << ClsName->getName() + << "' and not the class directly"; + + RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); + report->addRange(ME->getSourceRange()); + C.EmitReport(report); +} + +//===----------------------------------------------------------------------===// // Check registration. //===----------------------------------------------------------------------===// @@ -536,4 +596,5 @@ void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) { RegisterNSErrorChecks(BR, Eng, D); RegisterNSAutoreleasePoolChecks(Eng); + Eng.registerCheck(new ClassReleaseChecker(Ctx)); } diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m index dfea2e7738..76149bc021 100644 --- a/test/Analysis/retain-release.m +++ b/test/Analysis/retain-release.m @@ -1162,6 +1162,19 @@ void rdar7306898(void) { } //===----------------------------------------------------------------------===// +// <rdar://problem/7252064> sending 'release', 'retain', etc. to a Class +// directly is not likely what the user intended +//===----------------------------------------------------------------------===// + +@interface RDar7252064 : NSObject @end +void rdar7252064(void) { + [RDar7252064 release]; // expected-warning{{The 'release' message should be sent to instances of class 'RDar7252064' and not the class directly}} + [RDar7252064 retain]; // expected-warning{{The 'retain' message should be sent to instances of class 'RDar7252064' and not the class directly}} + [RDar7252064 autorelease]; // expected-warning{{The 'autorelease' message should be sent to instances of class 'RDar7252064' and not the class directly}} + [NSAutoreleasePool drain]; // expected-warning{{method '+drain' not found}} expected-warning{{The 'drain' message should be sent to instances of class 'NSAutoreleasePool' and not the class directly}} +} + +//===----------------------------------------------------------------------===// // Tests of ownership attributes. //===----------------------------------------------------------------------===// |