38 #include "llvm/ADT/DenseMap.h"
39 #include "llvm/ADT/SetVector.h"
40 #include "llvm/ADT/SmallString.h"
42 using namespace clang;
53 CheckName checkName_MissingInvalidationMethod;
54 CheckName checkName_InstanceVariableInvalidation;
57 class IvarInvalidationCheckerImpl {
68 struct InvalidationInfo {
73 MethodSet InvalidationMethods;
75 InvalidationInfo() : IsInvalidated(
false) {}
77 InvalidationMethods.insert(MD);
80 bool needsInvalidation()
const {
81 return !InvalidationMethods.empty();
87 for (MethodSet::iterator I = InvalidationMethods.begin(),
88 E = InvalidationMethods.end(); I != E; ++I) {
98 typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
108 bool &CalledAnotherInvalidationMethod;
111 const MethToIvarMapTy &PropertySetterToIvarMap;
114 const MethToIvarMapTy &PropertyGetterToIvarMap;
117 const PropToIvarMapTy &PropertyToIvarMap;
125 const Expr *peel(
const Expr *E)
const;
128 bool isZero(
const Expr *E)
const;
146 void check(
const Expr *E);
149 MethodCrawler(IvarSet &InIVars,
150 bool &InCalledAnotherInvalidationMethod,
151 const MethToIvarMapTy &InPropertySetterToIvarMap,
152 const MethToIvarMapTy &InPropertyGetterToIvarMap,
153 const PropToIvarMapTy &InPropertyToIvarMap,
156 CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
157 PropertySetterToIvarMap(InPropertySetterToIvarMap),
158 PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
159 PropertyToIvarMap(InPropertyToIvarMap),
160 InvalidationMethod(nullptr),
163 void VisitStmt(
const Stmt *
S) { VisitChildren(S); }
169 void VisitChildren(
const Stmt *
S) {
170 for (
const Stmt *Child : S->children()) {
173 if (CalledAnotherInvalidationMethod)
184 InvalidationInfo &Out,
185 bool LookForPartial);
189 static bool trackIvar(
const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
198 IvarSet &TrackedIvars,
202 static void printIvar(llvm::raw_svector_ostream &os,
204 const IvarToPropMapTy &IvarToPopertyMap);
208 const IvarToPropMapTy &IvarToPopertyMap,
210 bool MissingDeclaration)
const;
211 void reportIvarNeedsInvalidation(
const ObjCIvarDecl *IvarD,
212 const IvarToPropMapTy &IvarToPopertyMap,
218 const ChecksFilter &Filter;
223 const ChecksFilter &InFilter) :
224 Mgr (InMgr), BR(InBR), Filter(InFilter) {}
229 static bool isInvalidationMethod(
const ObjCMethodDecl *M,
bool LookForPartial) {
231 if (!LookForPartial &&
232 Ann->getAnnotation() ==
"objc_instance_variable_invalidator")
234 if (LookForPartial &&
235 Ann->getAnnotation() ==
"objc_instance_variable_invalidator_partial")
241 void IvarInvalidationCheckerImpl::containsInvalidationMethod(
247 assert(!isa<ObjCImplementationDecl>(D));
251 for (
const auto *MDI : D->
methods())
252 if (isInvalidationMethod(MDI, Partial))
253 OutInfo.addInvalidationMethod(
254 cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
260 for (
const auto *I : InterfD->protocols())
261 containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
265 for (
const auto *Ext : InterfD->visible_extensions())
266 containsInvalidationMethod(Ext, OutInfo, Partial);
268 containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
274 for (
const auto *I : ProtD->protocols()) {
275 containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
283 bool IvarInvalidationCheckerImpl::trackIvar(
const ObjCIvarDecl *Iv,
284 IvarSet &TrackedIvars,
292 InvalidationInfo Info;
293 containsInvalidationMethod(IvInterf, Info,
false);
294 if (Info.needsInvalidation()) {
296 TrackedIvars[I] = Info;
304 const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
307 IvarSet &TrackedIvars,
316 if (TrackedIvars.count(IvarD)) {
320 if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
326 for (IvarSet::const_iterator I = TrackedIvars.begin(),
327 E = TrackedIvars.end(); I != E; ++I) {
329 StringRef IvarName = Iv->
getName();
331 if (IvarName == PropName)
336 llvm::raw_svector_ostream os(PropNameWithUnderscore);
337 os <<
'_' << PropName;
339 if (IvarName == PropNameWithUnderscore)
349 void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
351 const IvarToPropMapTy &IvarToPopertyMap) {
354 assert(PD &&
"Do we synthesize ivars for something other than properties?");
355 os <<
"Property "<< PD->
getName() <<
" ";
357 os <<
"Instance variable "<< IvarDecl->
getName() <<
" ";
363 void IvarInvalidationCheckerImpl::
377 trackIvar(Iv, Ivars, &FirstIvarDecl);
381 MethToIvarMapTy PropSetterToIvarMap;
382 MethToIvarMapTy PropGetterToIvarMap;
383 PropToIvarMapTy PropertyToIvarMap;
384 IvarToPropMapTy IvarToPopertyMap;
390 for (ObjCInterfaceDecl::PropertyMap::iterator
391 I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
394 const ObjCIvarDecl *
ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
401 PropertyToIvarMap[PD] =
ID;
402 IvarToPopertyMap[
ID] = PD;
408 PropSetterToIvarMap[SetterD] =
ID;
414 PropGetterToIvarMap[GetterD] =
ID;
423 InvalidationInfo PartialInfo;
424 containsInvalidationMethod(InterfaceD, PartialInfo,
true);
428 bool AtImplementationContainsAtLeastOnePartialInvalidationMethod =
false;
429 for (MethodSet::iterator
430 I = PartialInfo.InvalidationMethods.begin(),
431 E = PartialInfo.InvalidationMethods.end(); I != E; ++I) {
438 AtImplementationContainsAtLeastOnePartialInvalidationMethod =
true;
440 bool CalledAnotherInvalidationMethod =
false;
443 CalledAnotherInvalidationMethod,
447 BR.getContext()).VisitStmt(D->
getBody());
450 if (CalledAnotherInvalidationMethod)
461 InvalidationInfo Info;
462 containsInvalidationMethod(InterfaceD, Info,
false);
465 if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) {
466 if (Filter.check_MissingInvalidationMethod)
467 reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod,
468 FirstIvarDecl, IvarToPopertyMap, InterfaceD,
477 if (!Filter.check_InstanceVariableInvalidation)
481 bool AtImplementationContainsAtLeastOneInvalidationMethod =
false;
482 for (MethodSet::iterator I = Info.InvalidationMethods.begin(),
483 E = Info.InvalidationMethods.end(); I != E; ++I) {
490 AtImplementationContainsAtLeastOneInvalidationMethod =
true;
493 IvarSet IvarsI = Ivars;
495 bool CalledAnotherInvalidationMethod =
false;
496 MethodCrawler(IvarsI,
497 CalledAnotherInvalidationMethod,
501 BR.getContext()).VisitStmt(D->
getBody());
504 if (CalledAnotherInvalidationMethod)
508 for (IvarSet::const_iterator
509 I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I)
510 reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D);
515 if (!AtImplementationContainsAtLeastOneInvalidationMethod) {
516 if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
519 for (IvarSet::const_iterator
520 I = Ivars.begin(), E = Ivars.end(); I != E; ++I)
521 reportIvarNeedsInvalidation(I->first, IvarToPopertyMap,
nullptr);
524 reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
525 FirstIvarDecl, IvarToPopertyMap, InterfaceD,
531 void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
533 const IvarToPropMapTy &IvarToPopertyMap,
536 llvm::raw_svector_ostream os(sbuf);
537 assert(FirstIvarDecl);
538 printIvar(os, FirstIvarDecl, IvarToPopertyMap);
539 os <<
"needs to be invalidated; ";
540 if (MissingDeclaration)
541 os <<
"no invalidation method is declared for ";
543 os <<
"no invalidation method is defined in the @implementation for ";
549 BR.EmitBasicReport(FirstIvarDecl, CheckName,
"Incomplete invalidation",
554 void IvarInvalidationCheckerImpl::
556 const IvarToPropMapTy &IvarToPopertyMap,
559 llvm::raw_svector_ostream os(sbuf);
560 printIvar(os, IvarD, IvarToPopertyMap);
561 os <<
"needs to be invalidated or set to nil";
565 BR.getSourceManager(),
566 Mgr.getAnalysisDeclContext(MethodD));
567 BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
568 "Incomplete invalidation",
573 IvarD, Filter.checkName_InstanceVariableInvalidation,
580 void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
582 IvarSet::iterator I = IVars.find(Iv);
583 if (I != IVars.end()) {
587 if (!InvalidationMethod ||
588 (InvalidationMethod && I->second.hasMethod(InvalidationMethod)))
593 const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(
const Expr *E)
const {
602 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
608 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
613 MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
614 if (IvI != PropertyGetterToIvarMap.end())
615 markInvalidated(IvI->second);
619 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
626 PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
627 if (IvI != PropertyToIvarMap.end())
628 markInvalidated(IvI->second);
637 MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
638 if (IvI != PropertyGetterToIvarMap.end())
639 markInvalidated(IvI->second);
645 bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(
const Expr *E)
const {
652 void IvarInvalidationCheckerImpl::MethodCrawler::check(
const Expr *E) {
656 checkObjCIvarRefExpr(IvarRef);
661 checkObjCPropertyRefExpr(PropRef);
666 checkObjCMessageExpr(MsgExpr);
671 void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
683 if (isZero(BO->
getRHS())) {
694 void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
700 if (Receiver && isInvalidationMethod(MD,
false))
702 CalledAnotherInvalidationMethod =
true;
709 MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
710 if (IvI != PropertySetterToIvarMap.end()) {
711 markInvalidated(IvI->second);
718 InvalidationMethod = MD;
720 InvalidationMethod =
nullptr;
730 class IvarInvalidationChecker :
731 public Checker<check::ASTDecl<ObjCImplementationDecl> > {
737 IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
743 #define REGISTER_CHECKER(name) \
744 void ento::register##name(CheckerManager &mgr) { \
745 IvarInvalidationChecker *checker = \
746 mgr.registerChecker<IvarInvalidationChecker>(); \
747 checker->Filter.check_##name = true; \
748 checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \
const char *const CoreFoundationObjectiveC
StringRef getName() const
IdentifierInfo * getIdentifier() const
A helper class which wraps a boolean value set to false by default.
ObjCMethodDecl * getMethod(Selector Sel, bool isInstance, bool AllowHidden=false) const
bool isExplicitProperty() const
NullPointerConstantKind isNullPointerConstant(ASTContext &Ctx, NullPointerConstantValueDependence NPC) const
FieldDecl * getCanonicalDecl() override
Retrieves the canonical declaration of this field.
const ObjCInterfaceDecl * getContainingInterface() const
Return the class interface that this ivar is logically contained in; this is either the interface whe...
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
method_range methods() const
void collectPropertiesToImplement(PropertyMap &PM, PropertyDeclOrder &PO) const override
A builtin binary operation expression such as "x + y" or "x <= y".
Expr * IgnoreParenCasts() LLVM_READONLY
Represents an Objective-C protocol declaration.
#define REGISTER_CHECKER(name)
Represents an ObjC class declaration.
llvm::DenseMap< IdentifierInfo *, ObjCPropertyDecl * > PropertyMap
virtual Decl * getCanonicalDecl()
Retrieves the "canonical" declaration of the given declaration.
ObjCMethodDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
ID
Defines the set of possible language-specific address spaces.
const ObjCMethodDecl * getMethodDecl() const
bool isObjCSelfExpr() const
Check if this expression is the ObjC 'self' implicit parameter.
StringRef getName() const
Return the actual identifier string.
bool isInstanceMethod() const
An expression that sends a message to the given Objective-C object or class.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
Expression is not a Null pointer constant.
Stmt * getBody() const override
Retrieve the body of this method, if it has one.
bool getSynthesize() const
static PathDiagnosticLocation createEnd(const Stmt *S, const SourceManager &SM, const LocationOrAnalysisDeclContext LAC)
const ObjCInterfaceDecl * getClassInterface() const
Specifies that a value-dependent expression should be considered to never be a null pointer constant...
Represents one property declaration in an Objective-C interface.
llvm::SmallVector< ObjCPropertyDecl *, 8 > PropertyDeclOrder
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
ObjCIvarDecl * getNextIvar()
bool isImplicitProperty() const
ObjCIvarDecl * getPropertyIvarDecl() const
Selector getSelector() const
ObjCMethodDecl * getGetterMethodDecl() const
ObjCInterfaceDecl * getInterfaceDecl() const
ObjCMethodDecl * getSetterMethodDecl() const
bool hasBody() const override
Determine whether this method has a body.
unsigned getNumArgs() const
Return the number of actual arguments in this message, not counting the receiver. ...
ObjCPropertyDecl * getExplicitProperty() const
ObjCIvarRefExpr - A reference to an ObjC instance variable.
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
ObjCIvarDecl * all_declared_ivar_begin()
ObjCMethodDecl * getImplicitPropertySetter() const