123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757 |
- //===- IvarInvalidationChecker.cpp ------------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This checker implements annotation driven invalidation checking. If a class
- // contains a method annotated with 'objc_instance_variable_invalidator',
- // - (void) foo
- // __attribute__((annotate("objc_instance_variable_invalidator")));
- // all the "ivalidatable" instance variables of this class should be
- // invalidated. We call an instance variable ivalidatable if it is an object of
- // a class which contains an invalidation method. There could be multiple
- // methods annotated with such annotations per class, either one can be used
- // to invalidate the ivar. An ivar or property are considered to be
- // invalidated if they are being assigned 'nil' or an invalidation method has
- // 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 address 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 "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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 {
- struct ChecksFilter {
- /// Check for missing invalidation method declarations.
- DefaultBool check_MissingInvalidationMethod;
- /// Check that all ivars are invalidated.
- DefaultBool check_InstanceVariableInvalidation;
- CheckerNameRef checkName_MissingInvalidationMethod;
- CheckerNameRef checkName_InstanceVariableInvalidation;
- };
- class IvarInvalidationCheckerImpl {
- typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet;
- typedef llvm::DenseMap<const ObjCMethodDecl*,
- const ObjCIvarDecl*> MethToIvarMapTy;
- typedef llvm::DenseMap<const ObjCPropertyDecl*,
- const ObjCIvarDecl*> PropToIvarMapTy;
- typedef llvm::DenseMap<const ObjCIvarDecl*,
- const ObjCPropertyDecl*> IvarToPropMapTy;
- struct InvalidationInfo {
- /// Has the ivar been invalidated?
- bool IsInvalidated;
- /// The methods which can be used to invalidate the ivar.
- MethodSet InvalidationMethods;
- InvalidationInfo() : IsInvalidated(false) {}
- void addInvalidationMethod(const ObjCMethodDecl *MD) {
- InvalidationMethods.insert(MD);
- }
- bool needsInvalidation() const {
- return !InvalidationMethods.empty();
- }
- bool hasMethod(const ObjCMethodDecl *MD) {
- if (IsInvalidated)
- return true;
- for (MethodSet::iterator I = InvalidationMethods.begin(),
- E = InvalidationMethods.end(); I != E; ++I) {
- if (*I == MD) {
- IsInvalidated = true;
- return true;
- }
- }
- return false;
- }
- };
- 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).
- class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
- /// The set of Ivars which need to be invalidated.
- IvarSet &IVars;
- /// Flag is set as the result of a message send to another
- /// invalidation method.
- bool &CalledAnotherInvalidationMethod;
- /// Property setter to ivar mapping.
- const MethToIvarMapTy &PropertySetterToIvarMap;
- /// Property getter to ivar mapping.
- const MethToIvarMapTy &PropertyGetterToIvarMap;
- /// Property to ivar mapping.
- const PropToIvarMapTy &PropertyToIvarMap;
- /// The invalidation method being currently processed.
- const ObjCMethodDecl *InvalidationMethod;
- ASTContext &Ctx;
- /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
- const Expr *peel(const Expr *E) const;
- /// Does this expression represent zero: '0'?
- bool isZero(const Expr *E) const;
- /// Mark the given ivar as invalidated.
- void markInvalidated(const ObjCIvarDecl *Iv);
- /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
- /// invalidated.
- void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
- /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
- /// it as invalidated.
- void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
- /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
- /// if yes, marks it as invalidated.
- void checkObjCMessageExpr(const ObjCMessageExpr *ME);
- /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
- void check(const Expr *E);
- public:
- MethodCrawler(IvarSet &InIVars,
- bool &InCalledAnotherInvalidationMethod,
- const MethToIvarMapTy &InPropertySetterToIvarMap,
- const MethToIvarMapTy &InPropertyGetterToIvarMap,
- const PropToIvarMapTy &InPropertyToIvarMap,
- ASTContext &InCtx)
- : IVars(InIVars),
- CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
- PropertySetterToIvarMap(InPropertySetterToIvarMap),
- PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
- PropertyToIvarMap(InPropertyToIvarMap),
- InvalidationMethod(nullptr),
- Ctx(InCtx) {}
- void VisitStmt(const Stmt *S) { VisitChildren(S); }
- void VisitBinaryOperator(const BinaryOperator *BO);
- void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
- void VisitChildren(const Stmt *S) {
- for (const auto *Child : S->children()) {
- if (Child)
- this->Visit(Child);
- if (CalledAnotherInvalidationMethod)
- return;
- }
- }
- };
- /// 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,
- 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,
- 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
- /// ivar could be found.
- static const ObjCIvarDecl *findPropertyBackingIvar(
- const ObjCPropertyDecl *Prop,
- const ObjCInterfaceDecl *InterfaceD,
- 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(CheckerNameRef CheckName,
- 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:
- IvarInvalidationCheckerImpl(AnalysisManager& InMgr,
- BugReporter &InBR,
- const ChecksFilter &InFilter) :
- Mgr (InMgr), BR(InBR), Filter(InFilter) {}
- void visit(const ObjCImplementationDecl *D) const;
- };
- static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
- for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) {
- if (!LookForPartial &&
- Ann->getAnnotation() == "objc_instance_variable_invalidator")
- return true;
- if (LookForPartial &&
- Ann->getAnnotation() == "objc_instance_variable_invalidator_partial")
- return true;
- }
- return false;
- }
- 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 (const auto *MDI : D->methods())
- if (isInvalidationMethod(MDI, Partial))
- OutInfo.addInvalidationMethod(
- cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
- // If interface, check all parent protocols and super.
- if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
- // Visit all protocols.
- for (const auto *I : InterfD->protocols())
- containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
- // Visit all categories in case the invalidation method is declared in
- // a category.
- for (const auto *Ext : InterfD->visible_extensions())
- containsInvalidationMethod(Ext, OutInfo, Partial);
- containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
- return;
- }
- // If protocol, check all parent protocols.
- if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
- for (const auto *I : ProtD->protocols()) {
- containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
- }
- return;
- }
- }
- 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();
- InvalidationInfo Info;
- containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
- if (Info.needsInvalidation()) {
- const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
- TrackedIvars[I] = Info;
- if (!*FirstIvarDecl)
- *FirstIvarDecl = I;
- return true;
- }
- return false;
- }
- const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
- const ObjCPropertyDecl *Prop,
- const ObjCInterfaceDecl *InterfaceD,
- IvarSet &TrackedIvars,
- const ObjCIvarDecl **FirstIvarDecl) {
- const ObjCIvarDecl *IvarD = nullptr;
- // Lookup for the synthesized case.
- IvarD = Prop->getPropertyIvarDecl();
- // 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, FirstIvarDecl))
- return IvarD;
- }
- // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
- StringRef PropName = Prop->getIdentifier()->getName();
- for (IvarSet::const_iterator I = TrackedIvars.begin(),
- E = TrackedIvars.end(); I != E; ++I) {
- const ObjCIvarDecl *Iv = I->first;
- StringRef IvarName = Iv->getName();
- if (IvarName == PropName)
- return Iv;
- SmallString<128> PropNameWithUnderscore;
- {
- llvm::raw_svector_ostream os(PropNameWithUnderscore);
- os << '_' << PropName;
- }
- if (IvarName == PropNameWithUnderscore)
- return Iv;
- }
- // Note, this is a possible source of false positives. We could look at the
- // getter implementation to find the ivar when its name is not derived from
- // the property name.
- return nullptr;
- }
- 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;
- // 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 = nullptr;
- 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, &FirstIvarDecl);
- // Construct Property/Property Accessor to Ivar maps to assist checking if an
- // ivar which is backing a property has been reset.
- MethToIvarMapTy PropSetterToIvarMap;
- MethToIvarMapTy PropGetterToIvarMap;
- PropToIvarMapTy PropertyToIvarMap;
- IvarToPropMapTy IvarToPopertyMap;
- ObjCInterfaceDecl::PropertyMap 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;
- if (PD->isClassProperty())
- continue;
- const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
- &FirstIvarDecl);
- if (!ID)
- continue;
- // Store the mappings.
- PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
- PropertyToIvarMap[PD] = ID;
- IvarToPopertyMap[ID] = PD;
- // Find the setter and the getter.
- const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
- if (SetterD) {
- SetterD = SetterD->getCanonicalDecl();
- PropSetterToIvarMap[SetterD] = ID;
- }
- const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
- if (GetterD) {
- GetterD = GetterD->getCanonicalDecl();
- PropGetterToIvarMap[GetterD] = ID;
- }
- }
- // 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.
- bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false;
- 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()) {
- AtImplementationContainsAtLeastOnePartialInvalidationMethod = true;
- 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();
- }
- }
- // If all ivars have been invalidated by partial invalidators, there is
- // nothing to check here.
- if (Ivars.empty())
- return;
- // 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() && !PartialInfo.needsInvalidation()) {
- if (Filter.check_MissingInvalidationMethod)
- reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod,
- FirstIvarDecl, IvarToPopertyMap, InterfaceD,
- /*MissingDeclaration*/ true);
- // If there are no invalidation methods, there is no ivar validation work
- // to be done.
- return;
- }
- // Only check if Ivars are invalidated when InstanceVariableInvalidation
- // has been requested.
- if (!Filter.check_InstanceVariableInvalidation)
- return;
- // 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) {
- if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
- // Warn on the ivars that were not invalidated by the prrtial
- // invalidation methods.
- for (IvarSet::const_iterator
- I = Ivars.begin(), E = Ivars.end(); I != E; ++I)
- reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, nullptr);
- } else {
- // Otherwise, no invalidation methods were implemented.
- reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
- FirstIvarDecl, IvarToPopertyMap, InterfaceD,
- /*MissingDeclaration*/ false);
- }
- }
- }
- void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
- CheckerNameRef CheckName, 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, CheckName, "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";
- if (MethodD) {
- PathDiagnosticLocation MethodDecLocation =
- PathDiagnosticLocation::createEnd(MethodD->getBody(),
- BR.getSourceManager(),
- Mgr.getAnalysisDeclContext(MethodD));
- BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
- "Incomplete invalidation",
- categories::CoreFoundationObjectiveC, os.str(),
- MethodDecLocation);
- } else {
- BR.EmitBasicReport(
- IvarD, Filter.checkName_InstanceVariableInvalidation,
- "Incomplete invalidation", categories::CoreFoundationObjectiveC,
- os.str(),
- PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager()));
- }
- }
- 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.hasMethod(InvalidationMethod))
- IVars.erase(I);
- }
- }
- const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const {
- E = E->IgnoreParenCasts();
- if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
- E = POE->getSyntacticForm()->IgnoreParenCasts();
- if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
- E = OVE->getSourceExpr()->IgnoreParenCasts();
- return E;
- }
- void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
- const ObjCIvarRefExpr *IvarRef) {
- if (const Decl *D = IvarRef->getDecl())
- markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
- }
- void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
- const ObjCMessageExpr *ME) {
- const ObjCMethodDecl *MD = ME->getMethodDecl();
- if (MD) {
- MD = MD->getCanonicalDecl();
- MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
- if (IvI != PropertyGetterToIvarMap.end())
- markInvalidated(IvI->second);
- }
- }
- void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
- const ObjCPropertyRefExpr *PA) {
- if (PA->isExplicitProperty()) {
- const ObjCPropertyDecl *PD = PA->getExplicitProperty();
- if (PD) {
- PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
- PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
- if (IvI != PropertyToIvarMap.end())
- markInvalidated(IvI->second);
- return;
- }
- }
- if (PA->isImplicitProperty()) {
- const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
- if (MD) {
- MD = MD->getCanonicalDecl();
- MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
- if (IvI != PropertyGetterToIvarMap.end())
- markInvalidated(IvI->second);
- return;
- }
- }
- }
- bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
- E = peel(E);
- return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
- != Expr::NPCK_NotNull);
- }
- void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) {
- E = peel(E);
- if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
- checkObjCIvarRefExpr(IvarRef);
- return;
- }
- if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
- checkObjCPropertyRefExpr(PropRef);
- return;
- }
- if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
- checkObjCMessageExpr(MsgExpr);
- return;
- }
- }
- void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
- const BinaryOperator *BO) {
- VisitStmt(BO);
- // 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;
- if (isZero(BO->getRHS())) {
- check(BO->getLHS());
- return;
- }
- if (Opcode != BO_Assign && isZero(BO->getLHS())) {
- check(BO->getRHS());
- return;
- }
- }
- 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, /*LookForPartial*/ false))
- if (Receiver->isObjCSelfExpr()) {
- CalledAnotherInvalidationMethod = true;
- return;
- }
- // Check if we call a setter and set the property to 'nil'.
- if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
- MD = MD->getCanonicalDecl();
- MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
- if (IvI != PropertySetterToIvarMap.end()) {
- markInvalidated(IvI->second);
- return;
- }
- }
- // Check if we call the 'invalidation' routine on the ivar.
- if (Receiver) {
- InvalidationMethod = MD;
- check(Receiver->IgnoreParenCasts());
- InvalidationMethod = nullptr;
- }
- VisitStmt(ME);
- }
- } // end anonymous namespace
- // 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);
- }
- };
- } // end anonymous namespace
- void ento::registerIvarInvalidationModeling(CheckerManager &mgr) {
- mgr.registerChecker<IvarInvalidationChecker>();
- }
- bool ento::shouldRegisterIvarInvalidationModeling(const CheckerManager &mgr) {
- return true;
- }
- #define REGISTER_CHECKER(name) \
- void ento::register##name(CheckerManager &mgr) { \
- IvarInvalidationChecker *checker = \
- mgr.getChecker<IvarInvalidationChecker>(); \
- checker->Filter.check_##name = true; \
- checker->Filter.checkName_##name = mgr.getCurrentCheckerName(); \
- } \
- \
- bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
- REGISTER_CHECKER(InstanceVariableInvalidation)
- REGISTER_CHECKER(MissingInvalidationMethod)
|