]> granicus.if.org Git - clang/commitdiff
Add simple static analyzer checker to check for sending 'release', 'retain', etc...
authorTed Kremenek <kremenek@apple.com>
Fri, 20 Nov 2009 05:27:05 +0000 (05:27 +0000)
committerTed Kremenek <kremenek@apple.com>
Fri, 20 Nov 2009 05:27:05 +0000 (05:27 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89449 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Analysis/BasicObjCFoundationChecks.cpp
test/Analysis/retain-release.m

index c2ecfa1f417f0992f6c32b8e0e17ed8752147b50..dd50fe0849642cbf9db1f7ecdfac055fcd693195 100644 (file)
@@ -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<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));
 }
index dfea2e7738d08a5add2225041684b3d9ba4bcb35..76149bc021bc79773e09624299cb3abce438522f 100644 (file)
@@ -1161,6 +1161,19 @@ void rdar7306898(void) {
   NSNumber *number = [[NSNumber alloc] initWithInt:5]; // expected-warning{{leak}}
 }
 
+//===----------------------------------------------------------------------===//
+// <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.
 //===----------------------------------------------------------------------===//