#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"
#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"
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
#include "llvm/Support/Streams.h"
+#include <vector>
+
using namespace clang;
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;
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) {
}
virtual void HandleTopLevelDecl(Decl *D);
+ virtual void HandleTranslationUnit(TranslationUnit &TU);
+
void HandleCode(Decl* D, Stmt* Body, Actions actions);
};
}
}
+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.
// Dispatch on the actions.
for (Actions::iterator I = actions.begin(),
E = actions.end(); I != E; ++I)
- ((*I).getHead())(mgr);
+ (*I)(mgr);
}
//===----------------------------------------------------------------------===//
mgr.getCFG().viewCFG();
}
+static void ActionCheckObjCDealloc(AnalysisManager& mgr) {
+ BugReporter BR(mgr);
+ CheckObjCDealloc(cast<ObjCImplementationDecl>(mgr.getCodeDecl()), BR);
+}
+
//===----------------------------------------------------------------------===//
// AnalysisConsumer creation.
//===----------------------------------------------------------------------===//
default: break;
}
+ // Checks we always perform:
+ C->addObjCImplementationAction(&ActionCheckObjCDealloc);
+
return C.take();
}
class ParentMap;
class LiveVariables;
class BugReporter;
+class ObjCImplementationDecl;
void CheckDeadStores(LiveVariables& L, BugReporter& BR);
bool StandardWarnings,
const LangOptions& lopts);
+void CheckObjCDealloc(ObjCImplementationDecl* D, BugReporter& BR);
+
+
} // end namespace clang
#endif
#include <vector>
#include <list>
-
namespace clang {
class PathDiagnostic;
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
--- /dev/null
+//==- 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);
+ }
+}
+
} // 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.
//===----------------------------------------------------------------------===//
- (void)myMethod;
- (void)myMethod2;
@end
-@implementation MyClass
+
+@implementation MyClass // no-warning
- (void)myMethod
{
NSPanel *panel = [[NSPanel alloc] initWithContentRect:NSMakeRect(0, 0, 200, 200) styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:(BOOL)1];
- (NSString*) getShared;
+ (C1*) sharedInstance;
@end
-@implementation C1 : NSObject {}
+@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];
@interface SharedClass : NSObject
+ (id)sharedInstance;
@end
-@implementation SharedClass
+@implementation SharedClass // expected-warning {{Objective-C class 'SharedClass' lacks a 'dealloc' instance method}}
- (id)_init {
if ((self = [super init])) {