aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp319
1 files changed, 319 insertions, 0 deletions
diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
new file mode 100644
index 0000000000..b08a2fbcb2
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -0,0 +1,319 @@
+//=- IvarInvalidationChecker.cpp - -*- C++ ----*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker implements annotation driven invalidation checking. If a class
+// contains a method annotated with 'objc_instance_variable_invalidator',
+// - (void) foo
+// __attribute__((annotate("objc_instance_variable_invalidator")));
+// all the "ivalidatable" instance variables of this class should be
+// invalidated. We call an instance variable ivalidatable if it is an object of
+// a class which contains an invalidation method.
+//
+// Note, this checker currently only checks if an ivar was accessed by the
+// method, we do not currently support any deeper invalidation checking.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/StmtVisitor.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallString.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class IvarInvalidationChecker :
+ public Checker<check::ASTDecl<ObjCMethodDecl> > {
+
+ typedef llvm::DenseMap<const ObjCIvarDecl*, bool> IvarSet;
+ typedef llvm::DenseMap<const ObjCMethodDecl*,
+ const ObjCIvarDecl*> MethToIvarMapTy;
+ typedef llvm::DenseMap<const ObjCPropertyDecl*,
+ const ObjCIvarDecl*> PropToIvarMapTy;
+
+ /// Statement visitor, which walks the method body and flags the ivars
+ /// referenced in it (either directly or via property).
+ class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
+ const ObjCInterfaceDecl *InterfD;
+
+ /// The set of Ivars which need to be invalidated.
+ IvarSet &IVars;
+
+ /// Property setter to ivar mapping.
+ MethToIvarMapTy &PropertySetterToIvarMap;
+
+ // Property to ivar mapping.
+ PropToIvarMapTy &PropertyToIvarMap;
+
+ public:
+ MethodCrawler(const ObjCInterfaceDecl *InID,
+ IvarSet &InIVars, MethToIvarMapTy &InPropertySetterToIvarMap,
+ PropToIvarMapTy &InPropertyToIvarMap)
+ : InterfD(InID), IVars(InIVars),
+ PropertySetterToIvarMap(InPropertySetterToIvarMap),
+ PropertyToIvarMap(InPropertyToIvarMap) {}
+
+ void VisitStmt(const Stmt *S) { VisitChildren(S); }
+
+ void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
+
+ void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
+
+ void VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
+
+ void VisitChildren(const Stmt *S) {
+ for (Stmt::const_child_range I = S->children(); I; ++I)
+ if (*I)
+ static_cast<MethodCrawler*>(this)->Visit(*I);
+ }
+ };
+
+ /// Check if the any of the methods inside the interface are annotated with
+ /// the invalidation annotation.
+ bool containsInvalidationMethod(const ObjCContainerDecl *D) const;
+
+ /// Given the property declaration, and the list of tracked ivars, finds
+ /// the ivar backing the property when possible. Returns '0' when no such
+ /// ivar could be found.
+ static const ObjCIvarDecl *findPropertyBackingIvar(
+ const ObjCPropertyDecl *Prop,
+ const ObjCInterfaceDecl *InterfaceD,
+ IvarSet TrackedIvars);
+
+public:
+ void checkASTDecl(const ObjCMethodDecl *D, AnalysisManager& Mgr,
+ BugReporter &BR) const;
+
+};
+
+bool isInvalidationMethod(const ObjCMethodDecl *M) {
+ const AnnotateAttr *Ann = M->getAttr<AnnotateAttr>();
+ if (!Ann)
+ return false;
+ if (Ann->getAnnotation() == "objc_instance_variable_invalidator")
+ return true;
+ return false;
+}
+
+bool IvarInvalidationChecker::containsInvalidationMethod (
+ const ObjCContainerDecl *D) const {
+
+ // TODO: Cache the results.
+
+ if (!D)
+ return false;
+
+ // Check all methods.
+ for (ObjCContainerDecl::method_iterator
+ I = D->meth_begin(),
+ E = D->meth_end(); I != E; ++I) {
+ const ObjCMethodDecl *MDI = *I;
+ if (isInvalidationMethod(MDI))
+ return true;
+ }
+
+ // If interface, check all parent protocols and super.
+ // TODO: Visit all categories in case the invalidation method is declared in
+ // a category.
+ if (const ObjCInterfaceDecl *InterfaceD = dyn_cast<ObjCInterfaceDecl>(D)) {
+ for (ObjCInterfaceDecl::protocol_iterator
+ I = InterfaceD->protocol_begin(),
+ E = InterfaceD->protocol_end(); I != E; ++I) {
+ if (containsInvalidationMethod(*I))
+ return true;
+ }
+ return containsInvalidationMethod(InterfaceD->getSuperClass());
+ }
+
+ // If protocol, check all parent protocols.
+ if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
+ for (ObjCInterfaceDecl::protocol_iterator
+ I = ProtD->protocol_begin(),
+ E = ProtD->protocol_end(); I != E; ++I) {
+ if (containsInvalidationMethod(*I))
+ return true;
+ }
+ return false;
+ }
+
+ llvm_unreachable("One of the casts above should have succeeded.");
+}
+
+const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar(
+ const ObjCPropertyDecl *Prop,
+ const ObjCInterfaceDecl *InterfaceD,
+ IvarSet TrackedIvars) {
+ const ObjCIvarDecl *IvarD = 0;
+
+ // Lookup for the synthesized case.
+ IvarD = Prop->getPropertyIvarDecl();
+ if (IvarD)
+ return IvarD;
+
+ // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
+ StringRef PropName = Prop->getIdentifier()->getName();
+ for (IvarSet::const_iterator I = TrackedIvars.begin(),
+ E = TrackedIvars.end(); I != E; ++I) {
+ const ObjCIvarDecl *Iv = I->first;
+ StringRef IvarName = Iv->getName();
+
+ if (IvarName == PropName)
+ return Iv;
+
+ SmallString<128> PropNameWithUnderscore;
+ {
+ llvm::raw_svector_ostream os(PropNameWithUnderscore);
+ os << '_' << PropName;
+ }
+ if (IvarName == PropNameWithUnderscore.str())
+ return Iv;
+ }
+
+ // Note, this is a possible source of false positives. We could look at the
+ // getter implementation to find the ivar when its name is not derived from
+ // the property name.
+ return 0;
+}
+
+void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D,
+ AnalysisManager& Mgr,
+ BugReporter &BR) const {
+ // We are only interested in checking the cleanup methods.
+ if (!D->hasBody() || !isInvalidationMethod(D))
+ return;
+
+ // Collect all ivars that need cleanup.
+ IvarSet Ivars;
+ const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
+ for (ObjCInterfaceDecl::ivar_iterator
+ II = InterfaceD->ivar_begin(),
+ IE = InterfaceD->ivar_end(); II != IE; ++II) {
+ const ObjCIvarDecl *Iv = *II;
+ QualType IvQTy = Iv->getType();
+ const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
+ if (!IvTy)
+ continue;
+ const ObjCInterfaceDecl *IvInterf = IvTy->getObjectType()->getInterface();
+ if (containsInvalidationMethod(IvInterf))
+ Ivars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = false;
+ }
+
+ // Construct Property/Property Setter to Ivar maps to assist checking if an
+ // ivar which is backing a property has been reset.
+ MethToIvarMapTy PropSetterToIvarMap;
+ PropToIvarMapTy PropertyToIvarMap;
+ for (ObjCInterfaceDecl::prop_iterator
+ I = InterfaceD->prop_begin(),
+ E = InterfaceD->prop_end(); I != E; ++I) {
+ const ObjCPropertyDecl *PD = *I;
+
+ const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars);
+ if (!ID) {
+ continue;
+ }
+ // Find the setter.
+ const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
+ // If we don't know the setter, do not track this ivar.
+ if (!SetterD) {
+ Ivars[cast<ObjCIvarDecl>(ID->getCanonicalDecl())] = true;
+ continue;
+ }
+
+ // Store the mappings.
+ PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
+ SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
+ PropertyToIvarMap[PD] = ID;
+ PropSetterToIvarMap[SetterD] = ID;
+ }
+
+
+ // Check which ivars have been accessed by the method.
+ // We assume that if ivar was at least accessed, it was not forgotten.
+ MethodCrawler(InterfaceD, Ivars,
+ PropSetterToIvarMap, PropertyToIvarMap).VisitStmt(D->getBody());
+
+ // Warn on the ivars that were not accessed by the method.
+ for (IvarSet::const_iterator I = Ivars.begin(), E = Ivars.end(); I != E; ++I){
+ if (I->second == false) {
+ const ObjCIvarDecl *IvarDecl = I->first;
+
+ PathDiagnosticLocation IvarDecLocation =
+ PathDiagnosticLocation::createBegin(IvarDecl, BR.getSourceManager());
+
+ SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ os << "Ivar needs to be invalidated in the '" <<
+ D->getSelector().getAsString()<< "' method";
+
+ BR.EmitBasicReport(IvarDecl,
+ "Incomplete invalidation",
+ categories::CoreFoundationObjectiveC, os.str(),
+ IvarDecLocation);
+ }
+ }
+}
+
+/// Handle the case when an ivar is directly accessed.
+void IvarInvalidationChecker::MethodCrawler::VisitObjCIvarRefExpr(
+ const ObjCIvarRefExpr *IvarRef) {
+ const Decl *D = IvarRef->getDecl();
+ if (D)
+ IVars[cast<ObjCIvarDecl>(D->getCanonicalDecl())] = true;
+ VisitStmt(IvarRef);
+}
+
+
+/// Handle the case when the property backing ivar is set via a direct call
+/// to the setter.
+void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr(
+ const ObjCMessageExpr *ME) {
+ const ObjCMethodDecl *MD = ME->getMethodDecl();
+ if (MD) {
+ MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
+ IVars[PropertySetterToIvarMap[MD]] = true;
+ }
+ VisitStmt(ME);
+}
+
+/// Handle the case when the property backing ivar is set via the dot syntax.
+void IvarInvalidationChecker::MethodCrawler::VisitObjCPropertyRefExpr(
+ const ObjCPropertyRefExpr *PA) {
+
+ if (PA->isExplicitProperty()) {
+ const ObjCPropertyDecl *PD = PA->getExplicitProperty();
+ if (PD) {
+ PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
+ IVars[PropertyToIvarMap[PD]] = true;
+ VisitStmt(PA);
+ return;
+ }
+ }
+
+ if (PA->isImplicitProperty()) {
+ const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
+ if (MD) {
+ MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
+ IVars[PropertySetterToIvarMap[MD]] = true;
+ VisitStmt(PA);
+ return;
+ }
+ }
+ VisitStmt(PA);
+}
+}
+
+// Register the checker.
+void ento::registerIvarInvalidationChecker(CheckerManager &mgr) {
+ mgr.registerChecker<IvarInvalidationChecker>();
+}