10b57cec5SDimitry Andric //===- IvarInvalidationChecker.cpp ------------------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This checker implements annotation driven invalidation checking. If a class
100b57cec5SDimitry Andric // contains a method annotated with 'objc_instance_variable_invalidator',
110b57cec5SDimitry Andric // - (void) foo
120b57cec5SDimitry Andric // __attribute__((annotate("objc_instance_variable_invalidator")));
130b57cec5SDimitry Andric // all the "ivalidatable" instance variables of this class should be
140b57cec5SDimitry Andric // invalidated. We call an instance variable ivalidatable if it is an object of
150b57cec5SDimitry Andric // a class which contains an invalidation method. There could be multiple
160b57cec5SDimitry Andric // methods annotated with such annotations per class, either one can be used
170b57cec5SDimitry Andric // to invalidate the ivar. An ivar or property are considered to be
180b57cec5SDimitry Andric // invalidated if they are being assigned 'nil' or an invalidation method has
190b57cec5SDimitry Andric // been called on them. An invalidation method should either invalidate all
200b57cec5SDimitry Andric // the ivars or call another invalidation method (on self).
210b57cec5SDimitry Andric //
220b57cec5SDimitry Andric // Partial invalidor annotation allows to address cases when ivars are
230b57cec5SDimitry Andric // invalidated by other methods, which might or might not be called from
240b57cec5SDimitry Andric // the invalidation method. The checker checks that each invalidation
250b57cec5SDimitry Andric // method and all the partial methods cumulatively invalidate all ivars.
260b57cec5SDimitry Andric // __attribute__((annotate("objc_instance_variable_invalidator_partial")));
270b57cec5SDimitry Andric //
280b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
290b57cec5SDimitry Andric
300b57cec5SDimitry Andric #include "clang/AST/Attr.h"
310b57cec5SDimitry Andric #include "clang/AST/DeclObjC.h"
320b57cec5SDimitry Andric #include "clang/AST/StmtVisitor.h"
3306c3fb27SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
340b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
350b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
360b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
370b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
3806c3fb27SDimitry Andric #include "llvm/ADT/STLExtras.h"
390b57cec5SDimitry Andric #include "llvm/ADT/SetVector.h"
400b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
410b57cec5SDimitry Andric
420b57cec5SDimitry Andric using namespace clang;
430b57cec5SDimitry Andric using namespace ento;
440b57cec5SDimitry Andric
450b57cec5SDimitry Andric namespace {
460b57cec5SDimitry Andric struct ChecksFilter {
470b57cec5SDimitry Andric /// Check for missing invalidation method declarations.
4881ad6265SDimitry Andric bool check_MissingInvalidationMethod = false;
490b57cec5SDimitry Andric /// Check that all ivars are invalidated.
5081ad6265SDimitry Andric bool check_InstanceVariableInvalidation = false;
510b57cec5SDimitry Andric
52a7dea167SDimitry Andric CheckerNameRef checkName_MissingInvalidationMethod;
53a7dea167SDimitry Andric CheckerNameRef checkName_InstanceVariableInvalidation;
540b57cec5SDimitry Andric };
550b57cec5SDimitry Andric
560b57cec5SDimitry Andric class IvarInvalidationCheckerImpl {
570b57cec5SDimitry Andric typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet;
580b57cec5SDimitry Andric typedef llvm::DenseMap<const ObjCMethodDecl*,
590b57cec5SDimitry Andric const ObjCIvarDecl*> MethToIvarMapTy;
600b57cec5SDimitry Andric typedef llvm::DenseMap<const ObjCPropertyDecl*,
610b57cec5SDimitry Andric const ObjCIvarDecl*> PropToIvarMapTy;
620b57cec5SDimitry Andric typedef llvm::DenseMap<const ObjCIvarDecl*,
630b57cec5SDimitry Andric const ObjCPropertyDecl*> IvarToPropMapTy;
640b57cec5SDimitry Andric
650b57cec5SDimitry Andric struct InvalidationInfo {
660b57cec5SDimitry Andric /// Has the ivar been invalidated?
67*5f757f3fSDimitry Andric bool IsInvalidated = false;
680b57cec5SDimitry Andric
690b57cec5SDimitry Andric /// The methods which can be used to invalidate the ivar.
700b57cec5SDimitry Andric MethodSet InvalidationMethods;
710b57cec5SDimitry Andric
72*5f757f3fSDimitry Andric InvalidationInfo() = default;
addInvalidationMethod__anon7be946890111::IvarInvalidationCheckerImpl::InvalidationInfo730b57cec5SDimitry Andric void addInvalidationMethod(const ObjCMethodDecl *MD) {
740b57cec5SDimitry Andric InvalidationMethods.insert(MD);
750b57cec5SDimitry Andric }
760b57cec5SDimitry Andric
needsInvalidation__anon7be946890111::IvarInvalidationCheckerImpl::InvalidationInfo770b57cec5SDimitry Andric bool needsInvalidation() const {
780b57cec5SDimitry Andric return !InvalidationMethods.empty();
790b57cec5SDimitry Andric }
800b57cec5SDimitry Andric
hasMethod__anon7be946890111::IvarInvalidationCheckerImpl::InvalidationInfo810b57cec5SDimitry Andric bool hasMethod(const ObjCMethodDecl *MD) {
820b57cec5SDimitry Andric if (IsInvalidated)
830b57cec5SDimitry Andric return true;
8406c3fb27SDimitry Andric for (const ObjCMethodDecl *Curr : InvalidationMethods) {
8506c3fb27SDimitry Andric if (Curr == MD) {
860b57cec5SDimitry Andric IsInvalidated = true;
870b57cec5SDimitry Andric return true;
880b57cec5SDimitry Andric }
890b57cec5SDimitry Andric }
900b57cec5SDimitry Andric return false;
910b57cec5SDimitry Andric }
920b57cec5SDimitry Andric };
930b57cec5SDimitry Andric
940b57cec5SDimitry Andric typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
950b57cec5SDimitry Andric
960b57cec5SDimitry Andric /// Statement visitor, which walks the method body and flags the ivars
970b57cec5SDimitry Andric /// referenced in it (either directly or via property).
980b57cec5SDimitry Andric class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
990b57cec5SDimitry Andric /// The set of Ivars which need to be invalidated.
1000b57cec5SDimitry Andric IvarSet &IVars;
1010b57cec5SDimitry Andric
1020b57cec5SDimitry Andric /// Flag is set as the result of a message send to another
1030b57cec5SDimitry Andric /// invalidation method.
1040b57cec5SDimitry Andric bool &CalledAnotherInvalidationMethod;
1050b57cec5SDimitry Andric
1060b57cec5SDimitry Andric /// Property setter to ivar mapping.
1070b57cec5SDimitry Andric const MethToIvarMapTy &PropertySetterToIvarMap;
1080b57cec5SDimitry Andric
1090b57cec5SDimitry Andric /// Property getter to ivar mapping.
1100b57cec5SDimitry Andric const MethToIvarMapTy &PropertyGetterToIvarMap;
1110b57cec5SDimitry Andric
1120b57cec5SDimitry Andric /// Property to ivar mapping.
1130b57cec5SDimitry Andric const PropToIvarMapTy &PropertyToIvarMap;
1140b57cec5SDimitry Andric
1150b57cec5SDimitry Andric /// The invalidation method being currently processed.
1160b57cec5SDimitry Andric const ObjCMethodDecl *InvalidationMethod;
1170b57cec5SDimitry Andric
1180b57cec5SDimitry Andric ASTContext &Ctx;
1190b57cec5SDimitry Andric
1200b57cec5SDimitry Andric /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
1210b57cec5SDimitry Andric const Expr *peel(const Expr *E) const;
1220b57cec5SDimitry Andric
1230b57cec5SDimitry Andric /// Does this expression represent zero: '0'?
1240b57cec5SDimitry Andric bool isZero(const Expr *E) const;
1250b57cec5SDimitry Andric
1260b57cec5SDimitry Andric /// Mark the given ivar as invalidated.
1270b57cec5SDimitry Andric void markInvalidated(const ObjCIvarDecl *Iv);
1280b57cec5SDimitry Andric
1290b57cec5SDimitry Andric /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
1300b57cec5SDimitry Andric /// invalidated.
1310b57cec5SDimitry Andric void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
1320b57cec5SDimitry Andric
1330b57cec5SDimitry Andric /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
1340b57cec5SDimitry Andric /// it as invalidated.
1350b57cec5SDimitry Andric void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
1360b57cec5SDimitry Andric
1370b57cec5SDimitry Andric /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
1380b57cec5SDimitry Andric /// if yes, marks it as invalidated.
1390b57cec5SDimitry Andric void checkObjCMessageExpr(const ObjCMessageExpr *ME);
1400b57cec5SDimitry Andric
1410b57cec5SDimitry Andric /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
1420b57cec5SDimitry Andric void check(const Expr *E);
1430b57cec5SDimitry Andric
1440b57cec5SDimitry Andric public:
MethodCrawler(IvarSet & InIVars,bool & InCalledAnotherInvalidationMethod,const MethToIvarMapTy & InPropertySetterToIvarMap,const MethToIvarMapTy & InPropertyGetterToIvarMap,const PropToIvarMapTy & InPropertyToIvarMap,ASTContext & InCtx)1450b57cec5SDimitry Andric MethodCrawler(IvarSet &InIVars,
1460b57cec5SDimitry Andric bool &InCalledAnotherInvalidationMethod,
1470b57cec5SDimitry Andric const MethToIvarMapTy &InPropertySetterToIvarMap,
1480b57cec5SDimitry Andric const MethToIvarMapTy &InPropertyGetterToIvarMap,
1490b57cec5SDimitry Andric const PropToIvarMapTy &InPropertyToIvarMap,
1500b57cec5SDimitry Andric ASTContext &InCtx)
1510b57cec5SDimitry Andric : IVars(InIVars),
1520b57cec5SDimitry Andric CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
1530b57cec5SDimitry Andric PropertySetterToIvarMap(InPropertySetterToIvarMap),
1540b57cec5SDimitry Andric PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
1550b57cec5SDimitry Andric PropertyToIvarMap(InPropertyToIvarMap),
1560b57cec5SDimitry Andric InvalidationMethod(nullptr),
1570b57cec5SDimitry Andric Ctx(InCtx) {}
1580b57cec5SDimitry Andric
VisitStmt(const Stmt * S)1590b57cec5SDimitry Andric void VisitStmt(const Stmt *S) { VisitChildren(S); }
1600b57cec5SDimitry Andric
1610b57cec5SDimitry Andric void VisitBinaryOperator(const BinaryOperator *BO);
1620b57cec5SDimitry Andric
1630b57cec5SDimitry Andric void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
1640b57cec5SDimitry Andric
VisitChildren(const Stmt * S)1650b57cec5SDimitry Andric void VisitChildren(const Stmt *S) {
1660b57cec5SDimitry Andric for (const auto *Child : S->children()) {
1670b57cec5SDimitry Andric if (Child)
1680b57cec5SDimitry Andric this->Visit(Child);
1690b57cec5SDimitry Andric if (CalledAnotherInvalidationMethod)
1700b57cec5SDimitry Andric return;
1710b57cec5SDimitry Andric }
1720b57cec5SDimitry Andric }
1730b57cec5SDimitry Andric };
1740b57cec5SDimitry Andric
1750b57cec5SDimitry Andric /// Check if the any of the methods inside the interface are annotated with
1760b57cec5SDimitry Andric /// the invalidation annotation, update the IvarInfo accordingly.
1770b57cec5SDimitry Andric /// \param LookForPartial is set when we are searching for partial
1780b57cec5SDimitry Andric /// invalidators.
1790b57cec5SDimitry Andric static void containsInvalidationMethod(const ObjCContainerDecl *D,
1800b57cec5SDimitry Andric InvalidationInfo &Out,
1810b57cec5SDimitry Andric bool LookForPartial);
1820b57cec5SDimitry Andric
1830b57cec5SDimitry Andric /// Check if ivar should be tracked and add to TrackedIvars if positive.
1840b57cec5SDimitry Andric /// Returns true if ivar should be tracked.
1850b57cec5SDimitry Andric static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
1860b57cec5SDimitry Andric const ObjCIvarDecl **FirstIvarDecl);
1870b57cec5SDimitry Andric
1880b57cec5SDimitry Andric /// Given the property declaration, and the list of tracked ivars, finds
1890b57cec5SDimitry Andric /// the ivar backing the property when possible. Returns '0' when no such
1900b57cec5SDimitry Andric /// ivar could be found.
1910b57cec5SDimitry Andric static const ObjCIvarDecl *findPropertyBackingIvar(
1920b57cec5SDimitry Andric const ObjCPropertyDecl *Prop,
1930b57cec5SDimitry Andric const ObjCInterfaceDecl *InterfaceD,
1940b57cec5SDimitry Andric IvarSet &TrackedIvars,
1950b57cec5SDimitry Andric const ObjCIvarDecl **FirstIvarDecl);
1960b57cec5SDimitry Andric
1970b57cec5SDimitry Andric /// Print ivar name or the property if the given ivar backs a property.
1980b57cec5SDimitry Andric static void printIvar(llvm::raw_svector_ostream &os,
1990b57cec5SDimitry Andric const ObjCIvarDecl *IvarDecl,
2000b57cec5SDimitry Andric const IvarToPropMapTy &IvarToPopertyMap);
2010b57cec5SDimitry Andric
202a7dea167SDimitry Andric void reportNoInvalidationMethod(CheckerNameRef CheckName,
2030b57cec5SDimitry Andric const ObjCIvarDecl *FirstIvarDecl,
2040b57cec5SDimitry Andric const IvarToPropMapTy &IvarToPopertyMap,
2050b57cec5SDimitry Andric const ObjCInterfaceDecl *InterfaceD,
2060b57cec5SDimitry Andric bool MissingDeclaration) const;
2070b57cec5SDimitry Andric
2080b57cec5SDimitry Andric void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
2090b57cec5SDimitry Andric const IvarToPropMapTy &IvarToPopertyMap,
2100b57cec5SDimitry Andric const ObjCMethodDecl *MethodD) const;
2110b57cec5SDimitry Andric
2120b57cec5SDimitry Andric AnalysisManager& Mgr;
2130b57cec5SDimitry Andric BugReporter &BR;
2140b57cec5SDimitry Andric /// Filter on the checks performed.
2150b57cec5SDimitry Andric const ChecksFilter &Filter;
2160b57cec5SDimitry Andric
2170b57cec5SDimitry Andric public:
IvarInvalidationCheckerImpl(AnalysisManager & InMgr,BugReporter & InBR,const ChecksFilter & InFilter)2180b57cec5SDimitry Andric IvarInvalidationCheckerImpl(AnalysisManager& InMgr,
2190b57cec5SDimitry Andric BugReporter &InBR,
2200b57cec5SDimitry Andric const ChecksFilter &InFilter) :
2210b57cec5SDimitry Andric Mgr (InMgr), BR(InBR), Filter(InFilter) {}
2220b57cec5SDimitry Andric
2230b57cec5SDimitry Andric void visit(const ObjCImplementationDecl *D) const;
2240b57cec5SDimitry Andric };
2250b57cec5SDimitry Andric
isInvalidationMethod(const ObjCMethodDecl * M,bool LookForPartial)2260b57cec5SDimitry Andric static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
2270b57cec5SDimitry Andric for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) {
2280b57cec5SDimitry Andric if (!LookForPartial &&
2290b57cec5SDimitry Andric Ann->getAnnotation() == "objc_instance_variable_invalidator")
2300b57cec5SDimitry Andric return true;
2310b57cec5SDimitry Andric if (LookForPartial &&
2320b57cec5SDimitry Andric Ann->getAnnotation() == "objc_instance_variable_invalidator_partial")
2330b57cec5SDimitry Andric return true;
2340b57cec5SDimitry Andric }
2350b57cec5SDimitry Andric return false;
2360b57cec5SDimitry Andric }
2370b57cec5SDimitry Andric
containsInvalidationMethod(const ObjCContainerDecl * D,InvalidationInfo & OutInfo,bool Partial)2380b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::containsInvalidationMethod(
2390b57cec5SDimitry Andric const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
2400b57cec5SDimitry Andric
2410b57cec5SDimitry Andric if (!D)
2420b57cec5SDimitry Andric return;
2430b57cec5SDimitry Andric
2440b57cec5SDimitry Andric assert(!isa<ObjCImplementationDecl>(D));
2450b57cec5SDimitry Andric // TODO: Cache the results.
2460b57cec5SDimitry Andric
2470b57cec5SDimitry Andric // Check all methods.
2480b57cec5SDimitry Andric for (const auto *MDI : D->methods())
2490b57cec5SDimitry Andric if (isInvalidationMethod(MDI, Partial))
2500b57cec5SDimitry Andric OutInfo.addInvalidationMethod(
2510b57cec5SDimitry Andric cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
2520b57cec5SDimitry Andric
2530b57cec5SDimitry Andric // If interface, check all parent protocols and super.
2540b57cec5SDimitry Andric if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
2550b57cec5SDimitry Andric
2560b57cec5SDimitry Andric // Visit all protocols.
2570b57cec5SDimitry Andric for (const auto *I : InterfD->protocols())
2580b57cec5SDimitry Andric containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
2590b57cec5SDimitry Andric
2600b57cec5SDimitry Andric // Visit all categories in case the invalidation method is declared in
2610b57cec5SDimitry Andric // a category.
2620b57cec5SDimitry Andric for (const auto *Ext : InterfD->visible_extensions())
2630b57cec5SDimitry Andric containsInvalidationMethod(Ext, OutInfo, Partial);
2640b57cec5SDimitry Andric
2650b57cec5SDimitry Andric containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
2660b57cec5SDimitry Andric return;
2670b57cec5SDimitry Andric }
2680b57cec5SDimitry Andric
2690b57cec5SDimitry Andric // If protocol, check all parent protocols.
2700b57cec5SDimitry Andric if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
2710b57cec5SDimitry Andric for (const auto *I : ProtD->protocols()) {
2720b57cec5SDimitry Andric containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
2730b57cec5SDimitry Andric }
2740b57cec5SDimitry Andric return;
2750b57cec5SDimitry Andric }
2760b57cec5SDimitry Andric }
2770b57cec5SDimitry Andric
trackIvar(const ObjCIvarDecl * Iv,IvarSet & TrackedIvars,const ObjCIvarDecl ** FirstIvarDecl)2780b57cec5SDimitry Andric bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv,
2790b57cec5SDimitry Andric IvarSet &TrackedIvars,
2800b57cec5SDimitry Andric const ObjCIvarDecl **FirstIvarDecl) {
2810b57cec5SDimitry Andric QualType IvQTy = Iv->getType();
2820b57cec5SDimitry Andric const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
2830b57cec5SDimitry Andric if (!IvTy)
2840b57cec5SDimitry Andric return false;
2850b57cec5SDimitry Andric const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
2860b57cec5SDimitry Andric
2870b57cec5SDimitry Andric InvalidationInfo Info;
2880b57cec5SDimitry Andric containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
2890b57cec5SDimitry Andric if (Info.needsInvalidation()) {
2900b57cec5SDimitry Andric const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
2910b57cec5SDimitry Andric TrackedIvars[I] = Info;
2920b57cec5SDimitry Andric if (!*FirstIvarDecl)
2930b57cec5SDimitry Andric *FirstIvarDecl = I;
2940b57cec5SDimitry Andric return true;
2950b57cec5SDimitry Andric }
2960b57cec5SDimitry Andric return false;
2970b57cec5SDimitry Andric }
2980b57cec5SDimitry Andric
findPropertyBackingIvar(const ObjCPropertyDecl * Prop,const ObjCInterfaceDecl * InterfaceD,IvarSet & TrackedIvars,const ObjCIvarDecl ** FirstIvarDecl)2990b57cec5SDimitry Andric const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
3000b57cec5SDimitry Andric const ObjCPropertyDecl *Prop,
3010b57cec5SDimitry Andric const ObjCInterfaceDecl *InterfaceD,
3020b57cec5SDimitry Andric IvarSet &TrackedIvars,
3030b57cec5SDimitry Andric const ObjCIvarDecl **FirstIvarDecl) {
3040b57cec5SDimitry Andric const ObjCIvarDecl *IvarD = nullptr;
3050b57cec5SDimitry Andric
3060b57cec5SDimitry Andric // Lookup for the synthesized case.
3070b57cec5SDimitry Andric IvarD = Prop->getPropertyIvarDecl();
3080b57cec5SDimitry Andric // We only track the ivars/properties that are defined in the current
3090b57cec5SDimitry Andric // class (not the parent).
3100b57cec5SDimitry Andric if (IvarD && IvarD->getContainingInterface() == InterfaceD) {
3110b57cec5SDimitry Andric if (TrackedIvars.count(IvarD)) {
3120b57cec5SDimitry Andric return IvarD;
3130b57cec5SDimitry Andric }
3140b57cec5SDimitry Andric // If the ivar is synthesized we still want to track it.
3150b57cec5SDimitry Andric if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
3160b57cec5SDimitry Andric return IvarD;
3170b57cec5SDimitry Andric }
3180b57cec5SDimitry Andric
3190b57cec5SDimitry Andric // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
3200b57cec5SDimitry Andric StringRef PropName = Prop->getIdentifier()->getName();
32106c3fb27SDimitry Andric for (const ObjCIvarDecl *Iv : llvm::make_first_range(TrackedIvars)) {
3220b57cec5SDimitry Andric StringRef IvarName = Iv->getName();
3230b57cec5SDimitry Andric
3240b57cec5SDimitry Andric if (IvarName == PropName)
3250b57cec5SDimitry Andric return Iv;
3260b57cec5SDimitry Andric
3270b57cec5SDimitry Andric SmallString<128> PropNameWithUnderscore;
3280b57cec5SDimitry Andric {
3290b57cec5SDimitry Andric llvm::raw_svector_ostream os(PropNameWithUnderscore);
3300b57cec5SDimitry Andric os << '_' << PropName;
3310b57cec5SDimitry Andric }
3320b57cec5SDimitry Andric if (IvarName == PropNameWithUnderscore)
3330b57cec5SDimitry Andric return Iv;
3340b57cec5SDimitry Andric }
3350b57cec5SDimitry Andric
3360b57cec5SDimitry Andric // Note, this is a possible source of false positives. We could look at the
3370b57cec5SDimitry Andric // getter implementation to find the ivar when its name is not derived from
3380b57cec5SDimitry Andric // the property name.
3390b57cec5SDimitry Andric return nullptr;
3400b57cec5SDimitry Andric }
3410b57cec5SDimitry Andric
printIvar(llvm::raw_svector_ostream & os,const ObjCIvarDecl * IvarDecl,const IvarToPropMapTy & IvarToPopertyMap)3420b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
3430b57cec5SDimitry Andric const ObjCIvarDecl *IvarDecl,
3440b57cec5SDimitry Andric const IvarToPropMapTy &IvarToPopertyMap) {
3450b57cec5SDimitry Andric if (IvarDecl->getSynthesize()) {
3460b57cec5SDimitry Andric const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
3470b57cec5SDimitry Andric assert(PD &&"Do we synthesize ivars for something other than properties?");
3480b57cec5SDimitry Andric os << "Property "<< PD->getName() << " ";
3490b57cec5SDimitry Andric } else {
3500b57cec5SDimitry Andric os << "Instance variable "<< IvarDecl->getName() << " ";
3510b57cec5SDimitry Andric }
3520b57cec5SDimitry Andric }
3530b57cec5SDimitry Andric
3540b57cec5SDimitry Andric // Check that the invalidatable interfaces with ivars/properties implement the
3550b57cec5SDimitry Andric // invalidation methods.
3560b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::
visit(const ObjCImplementationDecl * ImplD) const3570b57cec5SDimitry Andric visit(const ObjCImplementationDecl *ImplD) const {
3580b57cec5SDimitry Andric // Collect all ivars that need cleanup.
3590b57cec5SDimitry Andric IvarSet Ivars;
3600b57cec5SDimitry Andric // Record the first Ivar needing invalidation; used in reporting when only
3610b57cec5SDimitry Andric // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
3620b57cec5SDimitry Andric // deterministic output.
3630b57cec5SDimitry Andric const ObjCIvarDecl *FirstIvarDecl = nullptr;
3640b57cec5SDimitry Andric const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface();
3650b57cec5SDimitry Andric
3660b57cec5SDimitry Andric // Collect ivars declared in this class, its extensions and its implementation
3670b57cec5SDimitry Andric ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
3680b57cec5SDimitry Andric for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
3690b57cec5SDimitry Andric Iv= Iv->getNextIvar())
3700b57cec5SDimitry Andric trackIvar(Iv, Ivars, &FirstIvarDecl);
3710b57cec5SDimitry Andric
3720b57cec5SDimitry Andric // Construct Property/Property Accessor to Ivar maps to assist checking if an
3730b57cec5SDimitry Andric // ivar which is backing a property has been reset.
3740b57cec5SDimitry Andric MethToIvarMapTy PropSetterToIvarMap;
3750b57cec5SDimitry Andric MethToIvarMapTy PropGetterToIvarMap;
3760b57cec5SDimitry Andric PropToIvarMapTy PropertyToIvarMap;
3770b57cec5SDimitry Andric IvarToPropMapTy IvarToPopertyMap;
3780b57cec5SDimitry Andric
3790b57cec5SDimitry Andric ObjCInterfaceDecl::PropertyMap PropMap;
380bdd1243dSDimitry Andric InterfaceD->collectPropertiesToImplement(PropMap);
3810b57cec5SDimitry Andric
38206c3fb27SDimitry Andric for (const ObjCPropertyDecl *PD : llvm::make_second_range(PropMap)) {
3830b57cec5SDimitry Andric if (PD->isClassProperty())
3840b57cec5SDimitry Andric continue;
3850b57cec5SDimitry Andric
3860b57cec5SDimitry Andric const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
3870b57cec5SDimitry Andric &FirstIvarDecl);
3880b57cec5SDimitry Andric if (!ID)
3890b57cec5SDimitry Andric continue;
3900b57cec5SDimitry Andric
3910b57cec5SDimitry Andric // Store the mappings.
3920b57cec5SDimitry Andric PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
3930b57cec5SDimitry Andric PropertyToIvarMap[PD] = ID;
3940b57cec5SDimitry Andric IvarToPopertyMap[ID] = PD;
3950b57cec5SDimitry Andric
3960b57cec5SDimitry Andric // Find the setter and the getter.
3970b57cec5SDimitry Andric const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
3980b57cec5SDimitry Andric if (SetterD) {
3990b57cec5SDimitry Andric SetterD = SetterD->getCanonicalDecl();
4000b57cec5SDimitry Andric PropSetterToIvarMap[SetterD] = ID;
4010b57cec5SDimitry Andric }
4020b57cec5SDimitry Andric
4030b57cec5SDimitry Andric const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
4040b57cec5SDimitry Andric if (GetterD) {
4050b57cec5SDimitry Andric GetterD = GetterD->getCanonicalDecl();
4060b57cec5SDimitry Andric PropGetterToIvarMap[GetterD] = ID;
4070b57cec5SDimitry Andric }
4080b57cec5SDimitry Andric }
4090b57cec5SDimitry Andric
4100b57cec5SDimitry Andric // If no ivars need invalidation, there is nothing to check here.
4110b57cec5SDimitry Andric if (Ivars.empty())
4120b57cec5SDimitry Andric return;
4130b57cec5SDimitry Andric
4140b57cec5SDimitry Andric // Find all partial invalidation methods.
4150b57cec5SDimitry Andric InvalidationInfo PartialInfo;
4160b57cec5SDimitry Andric containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true);
4170b57cec5SDimitry Andric
4180b57cec5SDimitry Andric // Remove ivars invalidated by the partial invalidation methods. They do not
4190b57cec5SDimitry Andric // need to be invalidated in the regular invalidation methods.
4200b57cec5SDimitry Andric bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false;
42106c3fb27SDimitry Andric for (const ObjCMethodDecl *InterfD : PartialInfo.InvalidationMethods) {
4220b57cec5SDimitry Andric // Get the corresponding method in the @implementation.
4230b57cec5SDimitry Andric const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
4240b57cec5SDimitry Andric InterfD->isInstanceMethod());
4250b57cec5SDimitry Andric if (D && D->hasBody()) {
4260b57cec5SDimitry Andric AtImplementationContainsAtLeastOnePartialInvalidationMethod = true;
4270b57cec5SDimitry Andric
4280b57cec5SDimitry Andric bool CalledAnotherInvalidationMethod = false;
4290b57cec5SDimitry Andric // The MethodCrowler is going to remove the invalidated ivars.
4300b57cec5SDimitry Andric MethodCrawler(Ivars,
4310b57cec5SDimitry Andric CalledAnotherInvalidationMethod,
4320b57cec5SDimitry Andric PropSetterToIvarMap,
4330b57cec5SDimitry Andric PropGetterToIvarMap,
4340b57cec5SDimitry Andric PropertyToIvarMap,
4350b57cec5SDimitry Andric BR.getContext()).VisitStmt(D->getBody());
4360b57cec5SDimitry Andric // If another invalidation method was called, trust that full invalidation
4370b57cec5SDimitry Andric // has occurred.
4380b57cec5SDimitry Andric if (CalledAnotherInvalidationMethod)
4390b57cec5SDimitry Andric Ivars.clear();
4400b57cec5SDimitry Andric }
4410b57cec5SDimitry Andric }
4420b57cec5SDimitry Andric
4430b57cec5SDimitry Andric // If all ivars have been invalidated by partial invalidators, there is
4440b57cec5SDimitry Andric // nothing to check here.
4450b57cec5SDimitry Andric if (Ivars.empty())
4460b57cec5SDimitry Andric return;
4470b57cec5SDimitry Andric
4480b57cec5SDimitry Andric // Find all invalidation methods in this @interface declaration and parents.
4490b57cec5SDimitry Andric InvalidationInfo Info;
4500b57cec5SDimitry Andric containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
4510b57cec5SDimitry Andric
4520b57cec5SDimitry Andric // Report an error in case none of the invalidation methods are declared.
4530b57cec5SDimitry Andric if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) {
4540b57cec5SDimitry Andric if (Filter.check_MissingInvalidationMethod)
4550b57cec5SDimitry Andric reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod,
4560b57cec5SDimitry Andric FirstIvarDecl, IvarToPopertyMap, InterfaceD,
4570b57cec5SDimitry Andric /*MissingDeclaration*/ true);
4580b57cec5SDimitry Andric // If there are no invalidation methods, there is no ivar validation work
4590b57cec5SDimitry Andric // to be done.
4600b57cec5SDimitry Andric return;
4610b57cec5SDimitry Andric }
4620b57cec5SDimitry Andric
4630b57cec5SDimitry Andric // Only check if Ivars are invalidated when InstanceVariableInvalidation
4640b57cec5SDimitry Andric // has been requested.
4650b57cec5SDimitry Andric if (!Filter.check_InstanceVariableInvalidation)
4660b57cec5SDimitry Andric return;
4670b57cec5SDimitry Andric
4680b57cec5SDimitry Andric // Check that all ivars are invalidated by the invalidation methods.
4690b57cec5SDimitry Andric bool AtImplementationContainsAtLeastOneInvalidationMethod = false;
47006c3fb27SDimitry Andric for (const ObjCMethodDecl *InterfD : Info.InvalidationMethods) {
4710b57cec5SDimitry Andric // Get the corresponding method in the @implementation.
4720b57cec5SDimitry Andric const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
4730b57cec5SDimitry Andric InterfD->isInstanceMethod());
4740b57cec5SDimitry Andric if (D && D->hasBody()) {
4750b57cec5SDimitry Andric AtImplementationContainsAtLeastOneInvalidationMethod = true;
4760b57cec5SDimitry Andric
4770b57cec5SDimitry Andric // Get a copy of ivars needing invalidation.
4780b57cec5SDimitry Andric IvarSet IvarsI = Ivars;
4790b57cec5SDimitry Andric
4800b57cec5SDimitry Andric bool CalledAnotherInvalidationMethod = false;
4810b57cec5SDimitry Andric MethodCrawler(IvarsI,
4820b57cec5SDimitry Andric CalledAnotherInvalidationMethod,
4830b57cec5SDimitry Andric PropSetterToIvarMap,
4840b57cec5SDimitry Andric PropGetterToIvarMap,
4850b57cec5SDimitry Andric PropertyToIvarMap,
4860b57cec5SDimitry Andric BR.getContext()).VisitStmt(D->getBody());
4870b57cec5SDimitry Andric // If another invalidation method was called, trust that full invalidation
4880b57cec5SDimitry Andric // has occurred.
4890b57cec5SDimitry Andric if (CalledAnotherInvalidationMethod)
4900b57cec5SDimitry Andric continue;
4910b57cec5SDimitry Andric
4920b57cec5SDimitry Andric // Warn on the ivars that were not invalidated by the method.
49306c3fb27SDimitry Andric for (const ObjCIvarDecl *Ivar : llvm::make_first_range(IvarsI))
49406c3fb27SDimitry Andric reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap, D);
4950b57cec5SDimitry Andric }
4960b57cec5SDimitry Andric }
4970b57cec5SDimitry Andric
4980b57cec5SDimitry Andric // Report an error in case none of the invalidation methods are implemented.
4990b57cec5SDimitry Andric if (!AtImplementationContainsAtLeastOneInvalidationMethod) {
5000b57cec5SDimitry Andric if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
5010b57cec5SDimitry Andric // Warn on the ivars that were not invalidated by the prrtial
5020b57cec5SDimitry Andric // invalidation methods.
50306c3fb27SDimitry Andric for (const ObjCIvarDecl *Ivar : llvm::make_first_range(Ivars))
50406c3fb27SDimitry Andric reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap, nullptr);
5050b57cec5SDimitry Andric } else {
5060b57cec5SDimitry Andric // Otherwise, no invalidation methods were implemented.
5070b57cec5SDimitry Andric reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
5080b57cec5SDimitry Andric FirstIvarDecl, IvarToPopertyMap, InterfaceD,
5090b57cec5SDimitry Andric /*MissingDeclaration*/ false);
5100b57cec5SDimitry Andric }
5110b57cec5SDimitry Andric }
5120b57cec5SDimitry Andric }
5130b57cec5SDimitry Andric
reportNoInvalidationMethod(CheckerNameRef CheckName,const ObjCIvarDecl * FirstIvarDecl,const IvarToPropMapTy & IvarToPopertyMap,const ObjCInterfaceDecl * InterfaceD,bool MissingDeclaration) const5140b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
515a7dea167SDimitry Andric CheckerNameRef CheckName, const ObjCIvarDecl *FirstIvarDecl,
5160b57cec5SDimitry Andric const IvarToPropMapTy &IvarToPopertyMap,
5170b57cec5SDimitry Andric const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const {
5180b57cec5SDimitry Andric SmallString<128> sbuf;
5190b57cec5SDimitry Andric llvm::raw_svector_ostream os(sbuf);
5200b57cec5SDimitry Andric assert(FirstIvarDecl);
5210b57cec5SDimitry Andric printIvar(os, FirstIvarDecl, IvarToPopertyMap);
5220b57cec5SDimitry Andric os << "needs to be invalidated; ";
5230b57cec5SDimitry Andric if (MissingDeclaration)
5240b57cec5SDimitry Andric os << "no invalidation method is declared for ";
5250b57cec5SDimitry Andric else
5260b57cec5SDimitry Andric os << "no invalidation method is defined in the @implementation for ";
5270b57cec5SDimitry Andric os << InterfaceD->getName();
5280b57cec5SDimitry Andric
5290b57cec5SDimitry Andric PathDiagnosticLocation IvarDecLocation =
5300b57cec5SDimitry Andric PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
5310b57cec5SDimitry Andric
5320b57cec5SDimitry Andric BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation",
5330b57cec5SDimitry Andric categories::CoreFoundationObjectiveC, os.str(),
5340b57cec5SDimitry Andric IvarDecLocation);
5350b57cec5SDimitry Andric }
5360b57cec5SDimitry Andric
5370b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::
reportIvarNeedsInvalidation(const ObjCIvarDecl * IvarD,const IvarToPropMapTy & IvarToPopertyMap,const ObjCMethodDecl * MethodD) const5380b57cec5SDimitry Andric reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
5390b57cec5SDimitry Andric const IvarToPropMapTy &IvarToPopertyMap,
5400b57cec5SDimitry Andric const ObjCMethodDecl *MethodD) const {
5410b57cec5SDimitry Andric SmallString<128> sbuf;
5420b57cec5SDimitry Andric llvm::raw_svector_ostream os(sbuf);
5430b57cec5SDimitry Andric printIvar(os, IvarD, IvarToPopertyMap);
5440b57cec5SDimitry Andric os << "needs to be invalidated or set to nil";
5450b57cec5SDimitry Andric if (MethodD) {
5460b57cec5SDimitry Andric PathDiagnosticLocation MethodDecLocation =
5470b57cec5SDimitry Andric PathDiagnosticLocation::createEnd(MethodD->getBody(),
5480b57cec5SDimitry Andric BR.getSourceManager(),
5490b57cec5SDimitry Andric Mgr.getAnalysisDeclContext(MethodD));
5500b57cec5SDimitry Andric BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
5510b57cec5SDimitry Andric "Incomplete invalidation",
5520b57cec5SDimitry Andric categories::CoreFoundationObjectiveC, os.str(),
5530b57cec5SDimitry Andric MethodDecLocation);
5540b57cec5SDimitry Andric } else {
5550b57cec5SDimitry Andric BR.EmitBasicReport(
5560b57cec5SDimitry Andric IvarD, Filter.checkName_InstanceVariableInvalidation,
5570b57cec5SDimitry Andric "Incomplete invalidation", categories::CoreFoundationObjectiveC,
5580b57cec5SDimitry Andric os.str(),
5590b57cec5SDimitry Andric PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager()));
5600b57cec5SDimitry Andric }
5610b57cec5SDimitry Andric }
5620b57cec5SDimitry Andric
markInvalidated(const ObjCIvarDecl * Iv)5630b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
5640b57cec5SDimitry Andric const ObjCIvarDecl *Iv) {
5650b57cec5SDimitry Andric IvarSet::iterator I = IVars.find(Iv);
5660b57cec5SDimitry Andric if (I != IVars.end()) {
5670b57cec5SDimitry Andric // If InvalidationMethod is present, we are processing the message send and
5680b57cec5SDimitry Andric // should ensure we are invalidating with the appropriate method,
5690b57cec5SDimitry Andric // otherwise, we are processing setting to 'nil'.
5700b57cec5SDimitry Andric if (!InvalidationMethod || I->second.hasMethod(InvalidationMethod))
5710b57cec5SDimitry Andric IVars.erase(I);
5720b57cec5SDimitry Andric }
5730b57cec5SDimitry Andric }
5740b57cec5SDimitry Andric
peel(const Expr * E) const5750b57cec5SDimitry Andric const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const {
5760b57cec5SDimitry Andric E = E->IgnoreParenCasts();
5770b57cec5SDimitry Andric if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
5780b57cec5SDimitry Andric E = POE->getSyntacticForm()->IgnoreParenCasts();
5790b57cec5SDimitry Andric if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
5800b57cec5SDimitry Andric E = OVE->getSourceExpr()->IgnoreParenCasts();
5810b57cec5SDimitry Andric return E;
5820b57cec5SDimitry Andric }
5830b57cec5SDimitry Andric
checkObjCIvarRefExpr(const ObjCIvarRefExpr * IvarRef)5840b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
5850b57cec5SDimitry Andric const ObjCIvarRefExpr *IvarRef) {
5860b57cec5SDimitry Andric if (const Decl *D = IvarRef->getDecl())
5870b57cec5SDimitry Andric markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
5880b57cec5SDimitry Andric }
5890b57cec5SDimitry Andric
checkObjCMessageExpr(const ObjCMessageExpr * ME)5900b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
5910b57cec5SDimitry Andric const ObjCMessageExpr *ME) {
5920b57cec5SDimitry Andric const ObjCMethodDecl *MD = ME->getMethodDecl();
5930b57cec5SDimitry Andric if (MD) {
5940b57cec5SDimitry Andric MD = MD->getCanonicalDecl();
5950b57cec5SDimitry Andric MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
5960b57cec5SDimitry Andric if (IvI != PropertyGetterToIvarMap.end())
5970b57cec5SDimitry Andric markInvalidated(IvI->second);
5980b57cec5SDimitry Andric }
5990b57cec5SDimitry Andric }
6000b57cec5SDimitry Andric
checkObjCPropertyRefExpr(const ObjCPropertyRefExpr * PA)6010b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
6020b57cec5SDimitry Andric const ObjCPropertyRefExpr *PA) {
6030b57cec5SDimitry Andric
6040b57cec5SDimitry Andric if (PA->isExplicitProperty()) {
6050b57cec5SDimitry Andric const ObjCPropertyDecl *PD = PA->getExplicitProperty();
6060b57cec5SDimitry Andric if (PD) {
6070b57cec5SDimitry Andric PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
6080b57cec5SDimitry Andric PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
6090b57cec5SDimitry Andric if (IvI != PropertyToIvarMap.end())
6100b57cec5SDimitry Andric markInvalidated(IvI->second);
6110b57cec5SDimitry Andric return;
6120b57cec5SDimitry Andric }
6130b57cec5SDimitry Andric }
6140b57cec5SDimitry Andric
6150b57cec5SDimitry Andric if (PA->isImplicitProperty()) {
6160b57cec5SDimitry Andric const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
6170b57cec5SDimitry Andric if (MD) {
6180b57cec5SDimitry Andric MD = MD->getCanonicalDecl();
6190b57cec5SDimitry Andric MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
6200b57cec5SDimitry Andric if (IvI != PropertyGetterToIvarMap.end())
6210b57cec5SDimitry Andric markInvalidated(IvI->second);
6220b57cec5SDimitry Andric return;
6230b57cec5SDimitry Andric }
6240b57cec5SDimitry Andric }
6250b57cec5SDimitry Andric }
6260b57cec5SDimitry Andric
isZero(const Expr * E) const6270b57cec5SDimitry Andric bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
6280b57cec5SDimitry Andric E = peel(E);
6290b57cec5SDimitry Andric
6300b57cec5SDimitry Andric return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
6310b57cec5SDimitry Andric != Expr::NPCK_NotNull);
6320b57cec5SDimitry Andric }
6330b57cec5SDimitry Andric
check(const Expr * E)6340b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) {
6350b57cec5SDimitry Andric E = peel(E);
6360b57cec5SDimitry Andric
6370b57cec5SDimitry Andric if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
6380b57cec5SDimitry Andric checkObjCIvarRefExpr(IvarRef);
6390b57cec5SDimitry Andric return;
6400b57cec5SDimitry Andric }
6410b57cec5SDimitry Andric
6420b57cec5SDimitry Andric if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
6430b57cec5SDimitry Andric checkObjCPropertyRefExpr(PropRef);
6440b57cec5SDimitry Andric return;
6450b57cec5SDimitry Andric }
6460b57cec5SDimitry Andric
6470b57cec5SDimitry Andric if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
6480b57cec5SDimitry Andric checkObjCMessageExpr(MsgExpr);
6490b57cec5SDimitry Andric return;
6500b57cec5SDimitry Andric }
6510b57cec5SDimitry Andric }
6520b57cec5SDimitry Andric
VisitBinaryOperator(const BinaryOperator * BO)6530b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
6540b57cec5SDimitry Andric const BinaryOperator *BO) {
6550b57cec5SDimitry Andric VisitStmt(BO);
6560b57cec5SDimitry Andric
6570b57cec5SDimitry Andric // Do we assign/compare against zero? If yes, check the variable we are
6580b57cec5SDimitry Andric // assigning to.
6590b57cec5SDimitry Andric BinaryOperatorKind Opcode = BO->getOpcode();
6600b57cec5SDimitry Andric if (Opcode != BO_Assign &&
6610b57cec5SDimitry Andric Opcode != BO_EQ &&
6620b57cec5SDimitry Andric Opcode != BO_NE)
6630b57cec5SDimitry Andric return;
6640b57cec5SDimitry Andric
6650b57cec5SDimitry Andric if (isZero(BO->getRHS())) {
6660b57cec5SDimitry Andric check(BO->getLHS());
6670b57cec5SDimitry Andric return;
6680b57cec5SDimitry Andric }
6690b57cec5SDimitry Andric
6700b57cec5SDimitry Andric if (Opcode != BO_Assign && isZero(BO->getLHS())) {
6710b57cec5SDimitry Andric check(BO->getRHS());
6720b57cec5SDimitry Andric return;
6730b57cec5SDimitry Andric }
6740b57cec5SDimitry Andric }
6750b57cec5SDimitry Andric
VisitObjCMessageExpr(const ObjCMessageExpr * ME)6760b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
6770b57cec5SDimitry Andric const ObjCMessageExpr *ME) {
6780b57cec5SDimitry Andric const ObjCMethodDecl *MD = ME->getMethodDecl();
6790b57cec5SDimitry Andric const Expr *Receiver = ME->getInstanceReceiver();
6800b57cec5SDimitry Andric
6810b57cec5SDimitry Andric // Stop if we are calling '[self invalidate]'.
6820b57cec5SDimitry Andric if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false))
6830b57cec5SDimitry Andric if (Receiver->isObjCSelfExpr()) {
6840b57cec5SDimitry Andric CalledAnotherInvalidationMethod = true;
6850b57cec5SDimitry Andric return;
6860b57cec5SDimitry Andric }
6870b57cec5SDimitry Andric
6880b57cec5SDimitry Andric // Check if we call a setter and set the property to 'nil'.
6890b57cec5SDimitry Andric if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
6900b57cec5SDimitry Andric MD = MD->getCanonicalDecl();
6910b57cec5SDimitry Andric MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
6920b57cec5SDimitry Andric if (IvI != PropertySetterToIvarMap.end()) {
6930b57cec5SDimitry Andric markInvalidated(IvI->second);
6940b57cec5SDimitry Andric return;
6950b57cec5SDimitry Andric }
6960b57cec5SDimitry Andric }
6970b57cec5SDimitry Andric
6980b57cec5SDimitry Andric // Check if we call the 'invalidation' routine on the ivar.
6990b57cec5SDimitry Andric if (Receiver) {
7000b57cec5SDimitry Andric InvalidationMethod = MD;
7010b57cec5SDimitry Andric check(Receiver->IgnoreParenCasts());
7020b57cec5SDimitry Andric InvalidationMethod = nullptr;
7030b57cec5SDimitry Andric }
7040b57cec5SDimitry Andric
7050b57cec5SDimitry Andric VisitStmt(ME);
7060b57cec5SDimitry Andric }
7070b57cec5SDimitry Andric } // end anonymous namespace
7080b57cec5SDimitry Andric
7090b57cec5SDimitry Andric // Register the checkers.
7100b57cec5SDimitry Andric namespace {
7110b57cec5SDimitry Andric class IvarInvalidationChecker :
7120b57cec5SDimitry Andric public Checker<check::ASTDecl<ObjCImplementationDecl> > {
7130b57cec5SDimitry Andric public:
7140b57cec5SDimitry Andric ChecksFilter Filter;
7150b57cec5SDimitry Andric public:
checkASTDecl(const ObjCImplementationDecl * D,AnalysisManager & Mgr,BugReporter & BR) const7160b57cec5SDimitry Andric void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
7170b57cec5SDimitry Andric BugReporter &BR) const {
7180b57cec5SDimitry Andric IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
7190b57cec5SDimitry Andric Walker.visit(D);
7200b57cec5SDimitry Andric }
7210b57cec5SDimitry Andric };
7220b57cec5SDimitry Andric } // end anonymous namespace
7230b57cec5SDimitry Andric
registerIvarInvalidationModeling(CheckerManager & mgr)7240b57cec5SDimitry Andric void ento::registerIvarInvalidationModeling(CheckerManager &mgr) {
7250b57cec5SDimitry Andric mgr.registerChecker<IvarInvalidationChecker>();
7260b57cec5SDimitry Andric }
7270b57cec5SDimitry Andric
shouldRegisterIvarInvalidationModeling(const CheckerManager & mgr)7285ffd83dbSDimitry Andric bool ento::shouldRegisterIvarInvalidationModeling(const CheckerManager &mgr) {
7290b57cec5SDimitry Andric return true;
7300b57cec5SDimitry Andric }
7310b57cec5SDimitry Andric
7320b57cec5SDimitry Andric #define REGISTER_CHECKER(name) \
7330b57cec5SDimitry Andric void ento::register##name(CheckerManager &mgr) { \
7340b57cec5SDimitry Andric IvarInvalidationChecker *checker = \
7350b57cec5SDimitry Andric mgr.getChecker<IvarInvalidationChecker>(); \
7360b57cec5SDimitry Andric checker->Filter.check_##name = true; \
737a7dea167SDimitry Andric checker->Filter.checkName_##name = mgr.getCurrentCheckerName(); \
7380b57cec5SDimitry Andric } \
7390b57cec5SDimitry Andric \
7405ffd83dbSDimitry Andric bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
7410b57cec5SDimitry Andric
7420b57cec5SDimitry Andric REGISTER_CHECKER(InstanceVariableInvalidation)
7430b57cec5SDimitry Andric REGISTER_CHECKER(MissingInvalidationMethod)
744