diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp | 431 |
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) + |