[cfe-commits] r53075 - in /cfe/trunk: Driver/AnalysisConsumer.cpp include/clang/Analysis/LocalCheckers.h include/clang/Analysis/PathSensitive/BugReporter.h lib/Analysis/CheckObjCDealloc.cpp lib/Analysis/DeadStores.cpp test/Analysis/NSPanel.m test/Analysis/NSString.m

Ted Kremenek kremenek at apple.com
Wed Jul 2 21:29:21 PDT 2008


Author: kremenek
Date: Wed Jul  2 23:29:21 2008
New Revision: 53075

URL: http://llvm.org/viewvc/llvm-project?rev=53075&view=rev
Log:
Added static analysis check to see if a subclass of NSObject implements -dealloc, and whether or not that implementation calls [super dealloc].

Added:
    cfe/trunk/lib/Analysis/CheckObjCDealloc.cpp
Modified:
    cfe/trunk/Driver/AnalysisConsumer.cpp
    cfe/trunk/include/clang/Analysis/LocalCheckers.h
    cfe/trunk/include/clang/Analysis/PathSensitive/BugReporter.h
    cfe/trunk/lib/Analysis/DeadStores.cpp
    cfe/trunk/test/Analysis/NSPanel.m
    cfe/trunk/test/Analysis/NSString.m

Modified: cfe/trunk/Driver/AnalysisConsumer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/Driver/AnalysisConsumer.cpp?rev=53075&r1=53074&r2=53075&view=diff

==============================================================================
--- cfe/trunk/Driver/AnalysisConsumer.cpp (original)
+++ cfe/trunk/Driver/AnalysisConsumer.cpp Wed Jul  2 23:29:21 2008
@@ -17,7 +17,6 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclObjC.h"
 #include "llvm/Support/Compiler.h"
-#include "llvm/ADT/ImmutableList.h"
 #include "llvm/ADT/OwningPtr.h"
 #include "clang/AST/CFG.h"
 #include "clang/Analysis/Analyses/LiveVariables.h"
@@ -25,6 +24,7 @@
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/AST/ParentMap.h"
+#include "clang/AST/TranslationUnit.h"
 #include "clang/Analysis/PathSensitive/BugReporter.h"
 #include "clang/Analysis/Analyses/LiveVariables.h"
 #include "clang/Analysis/LocalCheckers.h"
@@ -32,6 +32,8 @@
 #include "clang/Analysis/PathSensitive/GRExprEngine.h"
 #include "llvm/Support/Streams.h"
 
+#include <vector>
+
 using namespace clang;
 
   
@@ -53,11 +55,10 @@
 namespace {
 
   class VISIBILITY_HIDDEN AnalysisConsumer : public ASTConsumer {
-    typedef llvm::ImmutableList<CodeAction> Actions;
+    typedef std::vector<CodeAction> Actions;
     Actions FunctionActions;
     Actions ObjCMethodActions;
-
-    Actions::Factory F;
+    Actions ObjCImplementationActions;
     
   public:
     const bool Visualize;
@@ -78,16 +79,19 @@
                      const std::string& fname,
                      const std::string& htmldir,
                      bool visualize, bool trim, bool analyzeAll) 
-      : FunctionActions(F.GetEmptyList()), ObjCMethodActions(F.GetEmptyList()),
-        Visualize(visualize), TrimGraph(trim), LOpts(lopts), Diags(diags),
+      : Visualize(visualize), TrimGraph(trim), LOpts(lopts), Diags(diags),
         Ctx(0), PP(pp), PPF(ppf),
         HTMLDir(htmldir),
         FName(fname),
         AnalyzeAll(analyzeAll) {}
     
     void addCodeAction(CodeAction action) {
-      FunctionActions = F.Concat(action, FunctionActions);
-      ObjCMethodActions = F.Concat(action, ObjCMethodActions);      
+      FunctionActions.push_back(action);
+      ObjCMethodActions.push_back(action);
+    }
+    
+    void addObjCImplementationAction(CodeAction action) {
+      ObjCImplementationActions.push_back(action);
     }
     
     virtual void Initialize(ASTContext &Context) {
@@ -95,6 +99,8 @@
     }
     
     virtual void HandleTopLevelDecl(Decl *D);
+    virtual void HandleTranslationUnit(TranslationUnit &TU);
+    
     void HandleCode(Decl* D, Stmt* Body, Actions actions);
   };
     
@@ -235,6 +241,18 @@
   }
 }
 
+void AnalysisConsumer::HandleTranslationUnit(TranslationUnit& TU) {
+
+  if (ObjCImplementationActions.empty())
+    return;
+    
+  for (TranslationUnit::iterator I = TU.begin(), E = TU.end(); I!=E; ++I) {
+    
+    if (ObjCImplementationDecl* ID = dyn_cast<ObjCImplementationDecl>(*I))
+      HandleCode(ID, 0, ObjCImplementationActions);
+  }
+}
+
 void AnalysisConsumer::HandleCode(Decl* D, Stmt* Body, Actions actions) {
   
   // Don't run the actions if an error has occured with parsing the file.
@@ -259,7 +277,7 @@
   // Dispatch on the actions.  
   for (Actions::iterator I = actions.begin(),
                          E = actions.end(); I != E; ++I)
-    ((*I).getHead())(mgr);  
+    (*I)(mgr);  
 }
 
 //===----------------------------------------------------------------------===//
@@ -351,6 +369,11 @@
   mgr.getCFG().viewCFG();  
 }
 
+static void ActionCheckObjCDealloc(AnalysisManager& mgr) {
+  BugReporter BR(mgr);
+  CheckObjCDealloc(cast<ObjCImplementationDecl>(mgr.getCodeDecl()), BR);  
+}
+
 //===----------------------------------------------------------------------===//
 // AnalysisConsumer creation.
 //===----------------------------------------------------------------------===//
@@ -401,6 +424,9 @@
       default: break;
     }
   
+  // Checks we always perform:
+  C->addObjCImplementationAction(&ActionCheckObjCDealloc);  
+  
   return C.take();
 }
 

Modified: cfe/trunk/include/clang/Analysis/LocalCheckers.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/LocalCheckers.h?rev=53075&r1=53074&r2=53075&view=diff

==============================================================================
--- cfe/trunk/include/clang/Analysis/LocalCheckers.h (original)
+++ cfe/trunk/include/clang/Analysis/LocalCheckers.h Wed Jul  2 23:29:21 2008
@@ -28,6 +28,7 @@
 class ParentMap;
 class LiveVariables;
 class BugReporter;
+class ObjCImplementationDecl;
   
 void CheckDeadStores(LiveVariables& L, BugReporter& BR); 
   
@@ -39,6 +40,9 @@
                                   bool StandardWarnings,
                                   const LangOptions& lopts); 
   
+void CheckObjCDealloc(ObjCImplementationDecl* D, BugReporter& BR);
+
+  
 } // end namespace clang
 
 #endif

Modified: cfe/trunk/include/clang/Analysis/PathSensitive/BugReporter.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/PathSensitive/BugReporter.h?rev=53075&r1=53074&r2=53075&view=diff

==============================================================================
--- cfe/trunk/include/clang/Analysis/PathSensitive/BugReporter.h (original)
+++ cfe/trunk/include/clang/Analysis/PathSensitive/BugReporter.h Wed Jul  2 23:29:21 2008
@@ -24,7 +24,6 @@
 #include <vector>
 #include <list>
 
-
 namespace clang {
   
 class PathDiagnostic;
@@ -286,6 +285,15 @@
   iterator end() { return Reports.end(); }
 };
   
+class SimpleBugType : public BugTypeCacheLocation {
+  const char* name;  
+public:
+  SimpleBugType(const char* n) : name(n) {}
+  
+  virtual const char* getName() const { return name; }
+  virtual const char* getDescription() const { return name; }
+};
+  
 } // end clang namespace
 
 #endif

Added: cfe/trunk/lib/Analysis/CheckObjCDealloc.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CheckObjCDealloc.cpp?rev=53075&view=auto

==============================================================================
--- cfe/trunk/lib/Analysis/CheckObjCDealloc.cpp (added)
+++ cfe/trunk/lib/Analysis/CheckObjCDealloc.cpp Wed Jul  2 23:29:21 2008
@@ -0,0 +1,118 @@
+//==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines a DeadStores, a flow-sensitive checker that looks for
+//  stores to variables that are no longer live.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/LocalCheckers.h"
+#include "clang/Analysis/PathDiagnostic.h"
+#include "clang/Analysis/PathSensitive/BugReporter.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/DeclObjC.h"
+#include <sstream>
+
+using namespace clang;
+
+static bool scan_dealloc(Stmt* S, Selector Dealloc) {  
+  
+  if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S))
+    if (ME->getSelector() == Dealloc)
+      if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts())
+        if (PreDefinedExpr* E = dyn_cast<PreDefinedExpr>(Receiver))
+          if (E->getIdentType() == PreDefinedExpr::ObjCSuper)
+            return true;
+
+  // Recurse to children.
+
+  for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I)
+    if (*I && scan_dealloc(*I, Dealloc))
+      return true;
+  
+  return false;
+}
+
+void clang::CheckObjCDealloc(ObjCImplementationDecl* D, BugReporter& BR) {
+
+  ASTContext& Ctx = BR.getContext();
+
+  // Determine if the class subclasses NSObject.
+  IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject");
+  ObjCInterfaceDecl* ID = D->getClassInterface();
+  
+  for ( ; ID ; ID = ID->getSuperClass())
+    if (ID->getIdentifier() == NSObjectII)
+      break;
+  
+  if (!ID)
+    return;
+  
+  // Get the "dealloc" selector.
+  IdentifierInfo* II = &Ctx.Idents.get("dealloc");
+  Selector S = Ctx.Selectors.getSelector(0, &II);
+  
+  ObjCMethodDecl* MD = 0;
+  
+  // Scan the instance methods for "dealloc".
+  for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
+       E = D->instmeth_end(); I!=E; ++I) {
+    
+    if ((*I)->getSelector() == S) {
+      MD = *I;
+      break;
+    }    
+  }
+  
+  if (!MD) { // No dealloc found.
+    
+    // FIXME: This code should be reduced to three lines if possible (Refactor).
+    SimpleBugType BT("missing -dealloc");
+    DiagCollector C(BT);
+    
+    std::ostringstream os;
+    os << "Objective-C class '" << D->getName()
+       << "' lacks a 'dealloc' instance method";
+    
+    Diagnostic& Diag = BR.getDiagnostic();    
+    Diag.Report(&C,
+                Ctx.getFullLoc(D->getLocStart()),
+                Diag.getCustomDiagID(Diagnostic::Warning, os.str().c_str()),
+                NULL, 0, NULL, 0);
+        
+    for (DiagCollector::iterator I = C.begin(), E = C.end(); I != E; ++I)
+      BR.EmitWarning(*I);
+    
+    return;
+  }
+  
+  // dealloc found.  Scan for missing [super dealloc].
+  if (MD->getBody() && !scan_dealloc(MD->getBody(), S)) {
+    
+    // FIXME: This code should be reduced to three lines if possible (Refactor).
+    SimpleBugType BT("missing [super dealloc]");
+    DiagCollector C(BT);
+    
+    std::ostringstream os;
+    os << "The 'dealloc' instance method in Objective-C class '" << D->getName()
+       << "' does not send a 'dealloc' message to it super class"
+           " (missing [super dealloc])";
+    
+    Diagnostic& Diag = BR.getDiagnostic();    
+    Diag.Report(&C,
+                Ctx.getFullLoc(MD->getLocStart()),
+                Diag.getCustomDiagID(Diagnostic::Warning, os.str().c_str()),
+                NULL, 0, NULL, 0);
+    
+    for (DiagCollector::iterator I = C.begin(), E = C.end(); I != E; ++I)
+      BR.EmitWarning(*I);
+  }    
+}
+

Modified: cfe/trunk/lib/Analysis/DeadStores.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/DeadStores.cpp?rev=53075&r1=53074&r2=53075&view=diff

==============================================================================
--- cfe/trunk/lib/Analysis/DeadStores.cpp (original)
+++ cfe/trunk/lib/Analysis/DeadStores.cpp Wed Jul  2 23:29:21 2008
@@ -144,23 +144,6 @@
 } // end anonymous namespace
 
 //===----------------------------------------------------------------------===//
-// BugReporter-based invocation of the Dead-Stores checker.
-//===----------------------------------------------------------------------===//
-  
-namespace {
-  
-class SimpleBugType : public BugTypeCacheLocation {
-  const char* name;  
-public:
-  SimpleBugType(const char* n) : name(n) {}
-  
-  virtual const char* getName() const {
-    return name;
-  }
-};
-} // end anonymous namespace
-
-//===----------------------------------------------------------------------===//
 // Driver function to invoke the Dead-Stores checker on a CFG.
 //===----------------------------------------------------------------------===//
 

Modified: cfe/trunk/test/Analysis/NSPanel.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/NSPanel.m?rev=53075&r1=53074&r2=53075&view=diff

==============================================================================
--- cfe/trunk/test/Analysis/NSPanel.m (original)
+++ cfe/trunk/test/Analysis/NSPanel.m Wed Jul  2 23:29:21 2008
@@ -67,7 +67,8 @@
 - (void)myMethod;
 - (void)myMethod2;
 @end
- at implementation MyClass
+
+ at implementation MyClass // no-warning
 - (void)myMethod
 {
   NSPanel *panel = [[NSPanel alloc] initWithContentRect:NSMakeRect(0, 0, 200, 200) styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:(BOOL)1];

Modified: cfe/trunk/test/Analysis/NSString.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/NSString.m?rev=53075&r1=53074&r2=53075&view=diff

==============================================================================
--- cfe/trunk/test/Analysis/NSString.m (original)
+++ cfe/trunk/test/Analysis/NSString.m Wed Jul  2 23:29:21 2008
@@ -143,7 +143,7 @@
 - (NSString*) getShared;
 + (C1*) sharedInstance;
 @end
- at implementation C1 : NSObject {}
+ at implementation C1 : NSObject {} // expected-warning{{Objective-C class 'C1' lacks a 'dealloc' instance method}}
 - (NSString*) getShared {
   static NSString* s = 0;
   if (!s) s = [[NSString alloc] init];    
@@ -161,7 +161,7 @@
 @interface SharedClass : NSObject
 + (id)sharedInstance;
 @end
- at implementation SharedClass
+ at implementation SharedClass // expected-warning {{Objective-C class 'SharedClass' lacks a 'dealloc' instance method}}
 
 - (id)_init {
     if ((self = [super init])) {





More information about the cfe-commits mailing list