summaryrefslogtreecommitdiffstats
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.cpp431
1 files changed, 310 insertions, 121 deletions
diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
index bf256cd..5ed28e9 100644
--- a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -20,25 +20,40 @@
// been called on them. An invalidation method should either invalidate all
// the ivars or call another invalidation method (on self).
//
+// Partial invalidor annotation allows to addess cases when ivars are
+// invalidated by other methods, which might or might not be called from
+// the invalidation method. The checker checks that each invalidation
+// method and all the partial methods cumulatively invalidate all ivars.
+// __attribute__((annotate("objc_instance_variable_invalidator_partial")));
+//
//===----------------------------------------------------------------------===//
#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/Attr.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/StmtVisitor.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallString.h"
using namespace clang;
using namespace ento;
namespace {
-class IvarInvalidationChecker :
- public Checker<check::ASTDecl<ObjCMethodDecl> > {
- typedef llvm::DenseSet<const ObjCMethodDecl*> MethodSet;
+struct ChecksFilter {
+ /// Check for missing invalidation method declarations.
+ DefaultBool check_MissingInvalidationMethod;
+ /// Check that all ivars are invalidated.
+ DefaultBool check_InstanceVariableInvalidation;
+};
+
+class IvarInvalidationCheckerImpl {
+
+ typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet;
typedef llvm::DenseMap<const ObjCMethodDecl*,
const ObjCIvarDecl*> MethToIvarMapTy;
typedef llvm::DenseMap<const ObjCPropertyDecl*,
@@ -47,14 +62,14 @@ class IvarInvalidationChecker :
const ObjCPropertyDecl*> IvarToPropMapTy;
- struct IvarInfo {
+ struct InvalidationInfo {
/// Has the ivar been invalidated?
bool IsInvalidated;
/// The methods which can be used to invalidate the ivar.
MethodSet InvalidationMethods;
- IvarInfo() : IsInvalidated(false) {}
+ InvalidationInfo() : IsInvalidated(false) {}
void addInvalidationMethod(const ObjCMethodDecl *MD) {
InvalidationMethods.insert(MD);
}
@@ -63,11 +78,7 @@ class IvarInvalidationChecker :
return !InvalidationMethods.empty();
}
- void markInvalidated() {
- IsInvalidated = true;
- }
-
- bool markInvalidated(const ObjCMethodDecl *MD) {
+ bool hasMethod(const ObjCMethodDecl *MD) {
if (IsInvalidated)
return true;
for (MethodSet::iterator I = InvalidationMethods.begin(),
@@ -79,13 +90,9 @@ class IvarInvalidationChecker :
}
return false;
}
-
- bool isInvalidated() const {
- return IsInvalidated;
- }
};
- typedef llvm::DenseMap<const ObjCIvarDecl*, IvarInfo> IvarSet;
+ typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
/// Statement visitor, which walks the method body and flags the ivars
/// referenced in it (either directly or via property).
@@ -168,12 +175,16 @@ class IvarInvalidationChecker :
/// Check if the any of the methods inside the interface are annotated with
/// the invalidation annotation, update the IvarInfo accordingly.
+ /// \param LookForPartial is set when we are searching for partial
+ /// invalidators.
static void containsInvalidationMethod(const ObjCContainerDecl *D,
- IvarInfo &Out);
+ InvalidationInfo &Out,
+ bool LookForPartial);
/// Check if ivar should be tracked and add to TrackedIvars if positive.
/// Returns true if ivar should be tracked.
- static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars);
+ static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
+ const ObjCIvarDecl **FirstIvarDecl);
/// Given the property declaration, and the list of tracked ivars, finds
/// the ivar backing the property when possible. Returns '0' when no such
@@ -181,54 +192,90 @@ class IvarInvalidationChecker :
static const ObjCIvarDecl *findPropertyBackingIvar(
const ObjCPropertyDecl *Prop,
const ObjCInterfaceDecl *InterfaceD,
- IvarSet &TrackedIvars);
+ IvarSet &TrackedIvars,
+ const ObjCIvarDecl **FirstIvarDecl);
+
+ /// Print ivar name or the property if the given ivar backs a property.
+ static void printIvar(llvm::raw_svector_ostream &os,
+ const ObjCIvarDecl *IvarDecl,
+ const IvarToPropMapTy &IvarToPopertyMap);
+
+ void reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
+ const IvarToPropMapTy &IvarToPopertyMap,
+ const ObjCInterfaceDecl *InterfaceD,
+ bool MissingDeclaration) const;
+ void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
+ const IvarToPropMapTy &IvarToPopertyMap,
+ const ObjCMethodDecl *MethodD) const;
+
+ AnalysisManager& Mgr;
+ BugReporter &BR;
+ /// Filter on the checks performed.
+ const ChecksFilter &Filter;
public:
- void checkASTDecl(const ObjCMethodDecl *D, AnalysisManager& Mgr,
- BugReporter &BR) const;
+ IvarInvalidationCheckerImpl(AnalysisManager& InMgr,
+ BugReporter &InBR,
+ const ChecksFilter &InFilter) :
+ Mgr (InMgr), BR(InBR), Filter(InFilter) {}
- // TODO: We are currently ignoring the ivars coming from class extensions.
+ void visit(const ObjCImplementationDecl *D) const;
};
-static bool isInvalidationMethod(const ObjCMethodDecl *M) {
+static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
for (specific_attr_iterator<AnnotateAttr>
AI = M->specific_attr_begin<AnnotateAttr>(),
AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) {
const AnnotateAttr *Ann = *AI;
- if (Ann->getAnnotation() == "objc_instance_variable_invalidator")
+ if (!LookForPartial &&
+ Ann->getAnnotation() == "objc_instance_variable_invalidator")
+ return true;
+ if (LookForPartial &&
+ Ann->getAnnotation() == "objc_instance_variable_invalidator_partial")
return true;
}
return false;
}
-void IvarInvalidationChecker::containsInvalidationMethod(
- const ObjCContainerDecl *D, IvarInfo &OutInfo) {
-
- // TODO: Cache the results.
+void IvarInvalidationCheckerImpl::containsInvalidationMethod(
+ const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
if (!D)
return;
+ assert(!isa<ObjCImplementationDecl>(D));
+ // TODO: Cache the results.
+
// 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))
+ if (isInvalidationMethod(MDI, Partial))
OutInfo.addInvalidationMethod(
cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
}
// 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)) {
+ if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
+
+ // Visit all protocols.
for (ObjCInterfaceDecl::protocol_iterator
- I = InterfaceD->protocol_begin(),
- E = InterfaceD->protocol_end(); I != E; ++I) {
- containsInvalidationMethod(*I, OutInfo);
+ I = InterfD->protocol_begin(),
+ E = InterfD->protocol_end(); I != E; ++I) {
+ containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial);
+ }
+
+ // Visit all categories in case the invalidation method is declared in
+ // a category.
+ for (ObjCInterfaceDecl::visible_extensions_iterator
+ Ext = InterfD->visible_extensions_begin(),
+ ExtEnd = InterfD->visible_extensions_end();
+ Ext != ExtEnd; ++Ext) {
+ containsInvalidationMethod(*Ext, OutInfo, Partial);
}
- containsInvalidationMethod(InterfaceD->getSuperClass(), OutInfo);
+
+ containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
return;
}
@@ -237,45 +284,52 @@ void IvarInvalidationChecker::containsInvalidationMethod(
for (ObjCInterfaceDecl::protocol_iterator
I = ProtD->protocol_begin(),
E = ProtD->protocol_end(); I != E; ++I) {
- containsInvalidationMethod(*I, OutInfo);
+ containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial);
}
return;
}
- llvm_unreachable("One of the casts above should have succeeded.");
+ return;
}
-bool IvarInvalidationChecker::trackIvar(const ObjCIvarDecl *Iv,
- IvarSet &TrackedIvars) {
+bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv,
+ IvarSet &TrackedIvars,
+ const ObjCIvarDecl **FirstIvarDecl) {
QualType IvQTy = Iv->getType();
const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
if (!IvTy)
return false;
const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
- IvarInfo Info;
- containsInvalidationMethod(IvInterf, Info);
+ InvalidationInfo Info;
+ containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
if (Info.needsInvalidation()) {
- TrackedIvars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = Info;
+ const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
+ TrackedIvars[I] = Info;
+ if (!*FirstIvarDecl)
+ *FirstIvarDecl = I;
return true;
}
return false;
}
-const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar(
+const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
const ObjCPropertyDecl *Prop,
const ObjCInterfaceDecl *InterfaceD,
- IvarSet &TrackedIvars) {
+ IvarSet &TrackedIvars,
+ const ObjCIvarDecl **FirstIvarDecl) {
const ObjCIvarDecl *IvarD = 0;
// Lookup for the synthesized case.
IvarD = Prop->getPropertyIvarDecl();
- if (IvarD) {
+ // We only track the ivars/properties that are defined in the current
+ // class (not the parent).
+ if (IvarD && IvarD->getContainingInterface() == InterfaceD) {
if (TrackedIvars.count(IvarD)) {
return IvarD;
}
// If the ivar is synthesized we still want to track it.
- if (trackIvar(IvarD, TrackedIvars))
+ if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
return IvarD;
}
@@ -304,22 +358,35 @@ const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar(
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;
+void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
+ const ObjCIvarDecl *IvarDecl,
+ const IvarToPropMapTy &IvarToPopertyMap) {
+ if (IvarDecl->getSynthesize()) {
+ const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
+ assert(PD &&"Do we synthesize ivars for something other than properties?");
+ os << "Property "<< PD->getName() << " ";
+ } else {
+ os << "Instance variable "<< IvarDecl->getName() << " ";
+ }
+}
+// Check that the invalidatable interfaces with ivars/properties implement the
+// invalidation methods.
+void IvarInvalidationCheckerImpl::
+visit(const ObjCImplementationDecl *ImplD) const {
// Collect all ivars that need cleanup.
IvarSet Ivars;
- const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
+ // Record the first Ivar needing invalidation; used in reporting when only
+ // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
+ // deterministic output.
+ const ObjCIvarDecl *FirstIvarDecl = 0;
+ const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface();
// Collect ivars declared in this class, its extensions and its implementation
ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
Iv= Iv->getNextIvar())
- trackIvar(Iv, Ivars);
+ trackIvar(Iv, Ivars, &FirstIvarDecl);
// Construct Property/Property Accessor to Ivar maps to assist checking if an
// ivar which is backing a property has been reset.
@@ -329,16 +396,17 @@ void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D,
IvarToPropMapTy IvarToPopertyMap;
ObjCInterfaceDecl::PropertyMap PropMap;
- InterfaceD->collectPropertiesToImplement(PropMap);
+ ObjCInterfaceDecl::PropertyDeclOrder PropOrder;
+ InterfaceD->collectPropertiesToImplement(PropMap, PropOrder);
for (ObjCInterfaceDecl::PropertyMap::iterator
I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
const ObjCPropertyDecl *PD = I->second;
- const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars);
- if (!ID) {
+ const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
+ &FirstIvarDecl);
+ if (!ID)
continue;
- }
// Store the mappings.
PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
@@ -359,66 +427,159 @@ void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D,
}
}
+ // If no ivars need invalidation, there is nothing to check here.
+ if (Ivars.empty())
+ return;
+
+ // Find all partial invalidation methods.
+ InvalidationInfo PartialInfo;
+ containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true);
+
+ // Remove ivars invalidated by the partial invalidation methods. They do not
+ // need to be invalidated in the regular invalidation methods.
+ for (MethodSet::iterator
+ I = PartialInfo.InvalidationMethods.begin(),
+ E = PartialInfo.InvalidationMethods.end(); I != E; ++I) {
+ const ObjCMethodDecl *InterfD = *I;
+
+ // Get the corresponding method in the @implementation.
+ const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
+ InterfD->isInstanceMethod());
+ if (D && D->hasBody()) {
+ bool CalledAnotherInvalidationMethod = false;
+ // The MethodCrowler is going to remove the invalidated ivars.
+ MethodCrawler(Ivars,
+ CalledAnotherInvalidationMethod,
+ PropSetterToIvarMap,
+ PropGetterToIvarMap,
+ PropertyToIvarMap,
+ BR.getContext()).VisitStmt(D->getBody());
+ // If another invalidation method was called, trust that full invalidation
+ // has occurred.
+ if (CalledAnotherInvalidationMethod)
+ Ivars.clear();
+ }
+ }
- // Check which ivars have been invalidated in the method body.
- bool CalledAnotherInvalidationMethod = false;
- MethodCrawler(Ivars,
- CalledAnotherInvalidationMethod,
- PropSetterToIvarMap,
- PropGetterToIvarMap,
- PropertyToIvarMap,
- BR.getContext()).VisitStmt(D->getBody());
+ // If all ivars have been invalidated by partial invalidators, there is
+ // nothing to check here.
+ if (Ivars.empty())
+ return;
- if (CalledAnotherInvalidationMethod)
+ // Find all invalidation methods in this @interface declaration and parents.
+ InvalidationInfo Info;
+ containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
+
+ // Report an error in case none of the invalidation methods are declared.
+ if (!Info.needsInvalidation()) {
+ if (Filter.check_MissingInvalidationMethod)
+ reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD,
+ /*MissingDeclaration*/ true);
+ // If there are no invalidation methods, there is no ivar validation work
+ // to be done.
return;
+ }
- // 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.isInvalidated()) {
- const ObjCIvarDecl *IvarDecl = I->first;
-
- PathDiagnosticLocation IvarDecLocation =
- PathDiagnosticLocation::createEnd(D->getBody(), BR.getSourceManager(),
- Mgr.getAnalysisDeclContext(D));
-
- SmallString<128> sbuf;
- llvm::raw_svector_ostream os(sbuf);
-
- // Construct the warning message.
- if (IvarDecl->getSynthesize()) {
- const ObjCPropertyDecl *PD = IvarToPopertyMap[IvarDecl];
- assert(PD &&
- "Do we synthesize ivars for something other than properties?");
- os << "Property "<< PD->getName() <<
- " needs to be invalidated or set to nil";
- } else {
- os << "Instance variable "<< IvarDecl->getName()
- << " needs to be invalidated or set to nil";
- }
+ // Only check if Ivars are invalidated when InstanceVariableInvalidation
+ // has been requested.
+ if (!Filter.check_InstanceVariableInvalidation)
+ return;
- BR.EmitBasicReport(D,
- "Incomplete invalidation",
- categories::CoreFoundationObjectiveC, os.str(),
- IvarDecLocation);
+ // Check that all ivars are invalidated by the invalidation methods.
+ bool AtImplementationContainsAtLeastOneInvalidationMethod = false;
+ for (MethodSet::iterator I = Info.InvalidationMethods.begin(),
+ E = Info.InvalidationMethods.end(); I != E; ++I) {
+ const ObjCMethodDecl *InterfD = *I;
+
+ // Get the corresponding method in the @implementation.
+ const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
+ InterfD->isInstanceMethod());
+ if (D && D->hasBody()) {
+ AtImplementationContainsAtLeastOneInvalidationMethod = true;
+
+ // Get a copy of ivars needing invalidation.
+ IvarSet IvarsI = Ivars;
+
+ bool CalledAnotherInvalidationMethod = false;
+ MethodCrawler(IvarsI,
+ CalledAnotherInvalidationMethod,
+ PropSetterToIvarMap,
+ PropGetterToIvarMap,
+ PropertyToIvarMap,
+ BR.getContext()).VisitStmt(D->getBody());
+ // If another invalidation method was called, trust that full invalidation
+ // has occurred.
+ if (CalledAnotherInvalidationMethod)
+ continue;
+
+ // Warn on the ivars that were not invalidated by the method.
+ for (IvarSet::const_iterator
+ I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I)
+ reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D);
}
}
+
+ // Report an error in case none of the invalidation methods are implemented.
+ if (!AtImplementationContainsAtLeastOneInvalidationMethod)
+ reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD,
+ /*MissingDeclaration*/ false);
}
-void IvarInvalidationChecker::MethodCrawler::markInvalidated(
+void IvarInvalidationCheckerImpl::
+reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
+ const IvarToPropMapTy &IvarToPopertyMap,
+ const ObjCInterfaceDecl *InterfaceD,
+ bool MissingDeclaration) const {
+ SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ assert(FirstIvarDecl);
+ printIvar(os, FirstIvarDecl, IvarToPopertyMap);
+ os << "needs to be invalidated; ";
+ if (MissingDeclaration)
+ os << "no invalidation method is declared for ";
+ else
+ os << "no invalidation method is defined in the @implementation for ";
+ os << InterfaceD->getName();
+
+ PathDiagnosticLocation IvarDecLocation =
+ PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
+
+ BR.EmitBasicReport(FirstIvarDecl, "Incomplete invalidation",
+ categories::CoreFoundationObjectiveC, os.str(),
+ IvarDecLocation);
+}
+
+void IvarInvalidationCheckerImpl::
+reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
+ const IvarToPropMapTy &IvarToPopertyMap,
+ const ObjCMethodDecl *MethodD) const {
+ SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ printIvar(os, IvarD, IvarToPopertyMap);
+ os << "needs to be invalidated or set to nil";
+ PathDiagnosticLocation MethodDecLocation =
+ PathDiagnosticLocation::createEnd(MethodD->getBody(),
+ BR.getSourceManager(),
+ Mgr.getAnalysisDeclContext(MethodD));
+ BR.EmitBasicReport(MethodD, "Incomplete invalidation",
+ categories::CoreFoundationObjectiveC, os.str(),
+ MethodDecLocation);
+}
+
+void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
const ObjCIvarDecl *Iv) {
IvarSet::iterator I = IVars.find(Iv);
if (I != IVars.end()) {
// If InvalidationMethod is present, we are processing the message send and
// should ensure we are invalidating with the appropriate method,
// otherwise, we are processing setting to 'nil'.
- if (InvalidationMethod)
- I->second.markInvalidated(InvalidationMethod);
- else
- I->second.markInvalidated();
+ if (!InvalidationMethod ||
+ (InvalidationMethod && I->second.hasMethod(InvalidationMethod)))
+ IVars.erase(I);
}
}
-const Expr *IvarInvalidationChecker::MethodCrawler::peel(const Expr *E) const {
+const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const {
E = E->IgnoreParenCasts();
if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
E = POE->getSyntacticForm()->IgnoreParenCasts();
@@ -427,13 +588,13 @@ const Expr *IvarInvalidationChecker::MethodCrawler::peel(const Expr *E) const {
return E;
}
-void IvarInvalidationChecker::MethodCrawler::checkObjCIvarRefExpr(
+void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
const ObjCIvarRefExpr *IvarRef) {
if (const Decl *D = IvarRef->getDecl())
markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
}
-void IvarInvalidationChecker::MethodCrawler::checkObjCMessageExpr(
+void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
const ObjCMessageExpr *ME) {
const ObjCMethodDecl *MD = ME->getMethodDecl();
if (MD) {
@@ -444,7 +605,7 @@ void IvarInvalidationChecker::MethodCrawler::checkObjCMessageExpr(
}
}
-void IvarInvalidationChecker::MethodCrawler::checkObjCPropertyRefExpr(
+void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
const ObjCPropertyRefExpr *PA) {
if (PA->isExplicitProperty()) {
@@ -470,14 +631,14 @@ void IvarInvalidationChecker::MethodCrawler::checkObjCPropertyRefExpr(
}
}
-bool IvarInvalidationChecker::MethodCrawler::isZero(const Expr *E) const {
+bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
E = peel(E);
return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
!= Expr::NPCK_NotNull);
}
-void IvarInvalidationChecker::MethodCrawler::check(const Expr *E) {
+void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) {
E = peel(E);
if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
@@ -496,28 +657,36 @@ void IvarInvalidationChecker::MethodCrawler::check(const Expr *E) {
}
}
-void IvarInvalidationChecker::MethodCrawler::VisitBinaryOperator(
+void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
const BinaryOperator *BO) {
VisitStmt(BO);
- if (BO->getOpcode() != BO_Assign)
+ // Do we assign/compare against zero? If yes, check the variable we are
+ // assigning to.
+ BinaryOperatorKind Opcode = BO->getOpcode();
+ if (Opcode != BO_Assign &&
+ Opcode != BO_EQ &&
+ Opcode != BO_NE)
return;
- // Do we assign zero?
- if (!isZero(BO->getRHS()))
- return;
+ if (isZero(BO->getRHS())) {
+ check(BO->getLHS());
+ return;
+ }
- // Check the variable we are assigning to.
- check(BO->getLHS());
+ if (Opcode != BO_Assign && isZero(BO->getLHS())) {
+ check(BO->getRHS());
+ return;
+ }
}
-void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr(
- const ObjCMessageExpr *ME) {
+void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
+ const ObjCMessageExpr *ME) {
const ObjCMethodDecl *MD = ME->getMethodDecl();
const Expr *Receiver = ME->getInstanceReceiver();
// Stop if we are calling '[self invalidate]'.
- if (Receiver && isInvalidationMethod(MD))
+ if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false))
if (Receiver->isObjCSelfExpr()) {
CalledAnotherInvalidationMethod = true;
return;
@@ -544,7 +713,27 @@ void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr(
}
}
-// Register the checker.
-void ento::registerIvarInvalidationChecker(CheckerManager &mgr) {
- mgr.registerChecker<IvarInvalidationChecker>();
+// Register the checkers.
+namespace {
+
+class IvarInvalidationChecker :
+ public Checker<check::ASTDecl<ObjCImplementationDecl> > {
+public:
+ ChecksFilter Filter;
+public:
+ void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
+ BugReporter &BR) const {
+ IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
+ Walker.visit(D);
+ }
+};
+}
+
+#define REGISTER_CHECKER(name) \
+void ento::register##name(CheckerManager &mgr) {\
+ mgr.registerChecker<IvarInvalidationChecker>()->Filter.check_##name = true;\
}
+
+REGISTER_CHECKER(InstanceVariableInvalidation)
+REGISTER_CHECKER(MissingInvalidationMethod)
+
OpenPOWER on IntegriCloud