From 50e837b3cbc9315b6808daabb96c5c7cccf11ea7 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Fri, 20 Nov 2009 05:27:05 +0000 Subject: [PATCH] Add simple static analyzer checker to check for sending 'release', 'retain', etc. directly to a class. Fixes . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89449 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Analysis/BasicObjCFoundationChecks.cpp | 61 ++++++++++++++++++++++ test/Analysis/retain-release.m | 13 +++++ 2 files changed, 74 insertions(+) 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" @@ -521,6 +522,65 @@ clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) { return new AuditCFRetainRelease(Ctx, BR); } +//===----------------------------------------------------------------------===// +// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. +//===----------------------------------------------------------------------===// + +namespace { +class VISIBILITY_HIDDEN ClassReleaseChecker : + public CheckerVisitor { + 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 @@ -1161,6 +1161,19 @@ void rdar7306898(void) { NSNumber *number = [[NSNumber alloc] initWithInt:5]; // expected-warning{{leak}} } +//===----------------------------------------------------------------------===// +// 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. //===----------------------------------------------------------------------===// -- 2.40.0