38 #include "llvm/ADT/DenseMap.h" 39 #include "llvm/ADT/SetVector.h" 40 #include "llvm/ADT/SmallString.h" 42 using namespace clang;
52 CheckName checkName_MissingInvalidationMethod;
53 CheckName checkName_InstanceVariableInvalidation;
56 class IvarInvalidationCheckerImpl {
63 const ObjCPropertyDecl*> IvarToPropMapTy;
65 struct InvalidationInfo {
70 MethodSet InvalidationMethods;
72 InvalidationInfo() : IsInvalidated(
false) {}
73 void addInvalidationMethod(
const ObjCMethodDecl *MD) {
74 InvalidationMethods.insert(MD);
77 bool needsInvalidation()
const {
78 return !InvalidationMethods.empty();
81 bool hasMethod(
const ObjCMethodDecl *MD) {
84 for (MethodSet::iterator I = InvalidationMethods.begin(),
85 E = InvalidationMethods.end(); I != E; ++I) {
95 typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
105 bool &CalledAnotherInvalidationMethod;
108 const MethToIvarMapTy &PropertySetterToIvarMap;
111 const MethToIvarMapTy &PropertyGetterToIvarMap;
114 const PropToIvarMapTy &PropertyToIvarMap;
117 const ObjCMethodDecl *InvalidationMethod;
122 const Expr *peel(
const Expr *E)
const;
128 void markInvalidated(
const ObjCIvarDecl *Iv);
143 void check(
const Expr *E);
146 MethodCrawler(IvarSet &InIVars,
147 bool &InCalledAnotherInvalidationMethod,
148 const MethToIvarMapTy &InPropertySetterToIvarMap,
149 const MethToIvarMapTy &InPropertyGetterToIvarMap,
150 const PropToIvarMapTy &InPropertyToIvarMap,
153 CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
154 PropertySetterToIvarMap(InPropertySetterToIvarMap),
155 PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
156 PropertyToIvarMap(InPropertyToIvarMap),
157 InvalidationMethod(nullptr),
160 void VisitStmt(
const Stmt *S) { VisitChildren(S); }
166 void VisitChildren(
const Stmt *S) {
167 for (
const auto *Child : S->
children()) {
170 if (CalledAnotherInvalidationMethod)
181 InvalidationInfo &Out,
182 bool LookForPartial);
186 static bool trackIvar(
const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
187 const ObjCIvarDecl **FirstIvarDecl);
192 static const ObjCIvarDecl *findPropertyBackingIvar(
193 const ObjCPropertyDecl *Prop,
195 IvarSet &TrackedIvars,
196 const ObjCIvarDecl **FirstIvarDecl);
199 static void printIvar(llvm::raw_svector_ostream &os,
200 const ObjCIvarDecl *IvarDecl,
201 const IvarToPropMapTy &IvarToPopertyMap);
204 const ObjCIvarDecl *FirstIvarDecl,
205 const IvarToPropMapTy &IvarToPopertyMap,
207 bool MissingDeclaration)
const;
209 void reportIvarNeedsInvalidation(
const ObjCIvarDecl *IvarD,
210 const IvarToPropMapTy &IvarToPopertyMap,
211 const ObjCMethodDecl *MethodD)
const;
216 const ChecksFilter &Filter;
221 const ChecksFilter &InFilter) :
222 Mgr (InMgr), BR(InBR), Filter(InFilter) {}
227 static bool isInvalidationMethod(
const ObjCMethodDecl *M,
bool LookForPartial) {
229 if (!LookForPartial &&
230 Ann->getAnnotation() ==
"objc_instance_variable_invalidator")
232 if (LookForPartial &&
233 Ann->getAnnotation() ==
"objc_instance_variable_invalidator_partial")
239 void IvarInvalidationCheckerImpl::containsInvalidationMethod(
245 assert(!isa<ObjCImplementationDecl>(D));
249 for (
const auto *MDI : D->
methods())
250 if (isInvalidationMethod(MDI, Partial))
251 OutInfo.addInvalidationMethod(
252 cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
258 for (
const auto *I : InterfD->protocols())
259 containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
263 for (
const auto *Ext : InterfD->visible_extensions())
264 containsInvalidationMethod(Ext, OutInfo, Partial);
266 containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
272 for (
const auto *I : ProtD->protocols()) {
273 containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
279 bool IvarInvalidationCheckerImpl::trackIvar(
const ObjCIvarDecl *Iv,
280 IvarSet &TrackedIvars,
281 const ObjCIvarDecl **FirstIvarDecl) {
288 InvalidationInfo Info;
289 containsInvalidationMethod(IvInterf, Info,
false);
290 if (Info.needsInvalidation()) {
292 TrackedIvars[I] = Info;
300 const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
301 const ObjCPropertyDecl *Prop,
303 IvarSet &TrackedIvars,
304 const ObjCIvarDecl **FirstIvarDecl) {
305 const ObjCIvarDecl *IvarD =
nullptr;
312 if (TrackedIvars.count(IvarD)) {
316 if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
322 for (IvarSet::const_iterator I = TrackedIvars.begin(),
323 E = TrackedIvars.end(); I != E; ++I) {
324 const ObjCIvarDecl *Iv = I->first;
325 StringRef IvarName = Iv->
getName();
327 if (IvarName == PropName)
332 llvm::raw_svector_ostream os(PropNameWithUnderscore);
333 os <<
'_' << PropName;
335 if (IvarName == PropNameWithUnderscore)
345 void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
346 const ObjCIvarDecl *IvarDecl,
347 const IvarToPropMapTy &IvarToPopertyMap) {
349 const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
350 assert(PD &&
"Do we synthesize ivars for something other than properties?");
351 os <<
"Property "<< PD->
getName() <<
" ";
353 os <<
"Instance variable "<< IvarDecl->
getName() <<
" ";
359 void IvarInvalidationCheckerImpl::
366 const ObjCIvarDecl *FirstIvarDecl =
nullptr;
373 trackIvar(Iv, Ivars, &FirstIvarDecl);
377 MethToIvarMapTy PropSetterToIvarMap;
378 MethToIvarMapTy PropGetterToIvarMap;
379 PropToIvarMapTy PropertyToIvarMap;
380 IvarToPropMapTy IvarToPopertyMap;
386 for (ObjCInterfaceDecl::PropertyMap::iterator
387 I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
388 const ObjCPropertyDecl *PD = I->second;
392 const ObjCIvarDecl *
ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
399 PropertyToIvarMap[PD] =
ID;
400 IvarToPopertyMap[
ID] = PD;
406 PropSetterToIvarMap[SetterD] =
ID;
412 PropGetterToIvarMap[GetterD] =
ID;
421 InvalidationInfo PartialInfo;
422 containsInvalidationMethod(InterfaceD, PartialInfo,
true);
426 bool AtImplementationContainsAtLeastOnePartialInvalidationMethod =
false;
427 for (MethodSet::iterator
428 I = PartialInfo.InvalidationMethods.begin(),
429 E = PartialInfo.InvalidationMethods.end(); I != E; ++I) {
430 const ObjCMethodDecl *InterfD = *I;
436 AtImplementationContainsAtLeastOnePartialInvalidationMethod =
true;
438 bool CalledAnotherInvalidationMethod =
false;
441 CalledAnotherInvalidationMethod,
445 BR.getContext()).VisitStmt(D->
getBody());
448 if (CalledAnotherInvalidationMethod)
459 InvalidationInfo Info;
460 containsInvalidationMethod(InterfaceD, Info,
false);
463 if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) {
464 if (Filter.check_MissingInvalidationMethod)
465 reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod,
466 FirstIvarDecl, IvarToPopertyMap, InterfaceD,
475 if (!Filter.check_InstanceVariableInvalidation)
479 bool AtImplementationContainsAtLeastOneInvalidationMethod =
false;
480 for (MethodSet::iterator I = Info.InvalidationMethods.begin(),
481 E = Info.InvalidationMethods.end(); I != E; ++I) {
482 const ObjCMethodDecl *InterfD = *I;
488 AtImplementationContainsAtLeastOneInvalidationMethod =
true;
491 IvarSet IvarsI = Ivars;
493 bool CalledAnotherInvalidationMethod =
false;
494 MethodCrawler(IvarsI,
495 CalledAnotherInvalidationMethod,
499 BR.getContext()).VisitStmt(D->
getBody());
502 if (CalledAnotherInvalidationMethod)
506 for (IvarSet::const_iterator
507 I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I)
508 reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D);
513 if (!AtImplementationContainsAtLeastOneInvalidationMethod) {
514 if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
517 for (IvarSet::const_iterator
518 I = Ivars.begin(), E = Ivars.end(); I != E; ++I)
519 reportIvarNeedsInvalidation(I->first, IvarToPopertyMap,
nullptr);
522 reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
523 FirstIvarDecl, IvarToPopertyMap, InterfaceD,
529 void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
531 const IvarToPropMapTy &IvarToPopertyMap,
534 llvm::raw_svector_ostream os(sbuf);
535 assert(FirstIvarDecl);
536 printIvar(os, FirstIvarDecl, IvarToPopertyMap);
537 os <<
"needs to be invalidated; ";
538 if (MissingDeclaration)
539 os <<
"no invalidation method is declared for ";
541 os <<
"no invalidation method is defined in the @implementation for ";
547 BR.EmitBasicReport(FirstIvarDecl, CheckName,
"Incomplete invalidation",
552 void IvarInvalidationCheckerImpl::
553 reportIvarNeedsInvalidation(
const ObjCIvarDecl *IvarD,
554 const IvarToPropMapTy &IvarToPopertyMap,
555 const ObjCMethodDecl *MethodD)
const {
557 llvm::raw_svector_ostream os(sbuf);
558 printIvar(os, IvarD, IvarToPopertyMap);
559 os <<
"needs to be invalidated or set to nil";
563 BR.getSourceManager(),
564 Mgr.getAnalysisDeclContext(MethodD));
565 BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
566 "Incomplete invalidation",
571 IvarD, Filter.checkName_InstanceVariableInvalidation,
578 void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
579 const ObjCIvarDecl *Iv) {
580 IvarSet::iterator I = IVars.find(Iv);
581 if (I != IVars.end()) {
585 if (!InvalidationMethod || I->second.hasMethod(InvalidationMethod))
590 const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(
const Expr *E)
const {
599 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
605 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
610 MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
611 if (IvI != PropertyGetterToIvarMap.end())
612 markInvalidated(IvI->second);
616 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
623 PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
624 if (IvI != PropertyToIvarMap.end())
625 markInvalidated(IvI->second);
634 MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
635 if (IvI != PropertyGetterToIvarMap.end())
636 markInvalidated(IvI->second);
649 void IvarInvalidationCheckerImpl::MethodCrawler::check(
const Expr *E) {
653 checkObjCIvarRefExpr(IvarRef);
658 checkObjCPropertyRefExpr(PropRef);
663 checkObjCMessageExpr(MsgExpr);
668 void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
675 if (Opcode != BO_Assign &&
691 void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
697 if (Receiver && isInvalidationMethod(MD,
false))
699 CalledAnotherInvalidationMethod =
true;
706 MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
707 if (IvI != PropertySetterToIvarMap.end()) {
708 markInvalidated(IvI->second);
715 InvalidationMethod = MD;
717 InvalidationMethod =
nullptr;
726 class IvarInvalidationChecker :
727 public Checker<check::ASTDecl<ObjCImplementationDecl> > {
733 IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
739 #define REGISTER_CHECKER(name) \ 740 void ento::register##name(CheckerManager &mgr) { \ 741 IvarInvalidationChecker *checker = \ 742 mgr.registerChecker<IvarInvalidationChecker>(); \ 743 checker->Filter.check_##name = true; \ 744 checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \ ObjCPropertyRefExpr - A dot-syntax expression to access an ObjC property.
const char *const CoreFoundationObjectiveC
A (possibly-)qualified type.
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
Stmt - This represents one statement.
A helper class which wraps a boolean value set to false by default.
llvm::SmallVector< ObjCPropertyDecl *, 8 > PropertyDeclOrder
Decl - This represents one declaration (or definition), e.g.
ObjCMethodDecl * getImplicitPropertySetter() const
bool isZero(ProgramStateRef State, const NonLoc &Val)
const T * getAs() const
Member-template getAs<specific type>'.
ObjCMethodDecl - Represents an instance or class method declaration.
ObjCPropertyDecl * getExplicitProperty() const
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
FieldDecl * getCanonicalDecl() override
Retrieves the canonical declaration of this field.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
bool isExplicitProperty() const
method_range methods() const
ObjCMethodDecl * getSetterMethodDecl() const
bool isClassProperty() const
void collectPropertiesToImplement(PropertyMap &PM, PropertyDeclOrder &PO) const override
This routine collects list of properties to be implemented in the class.
ObjCContainerDecl - Represents a container for method declarations.
A builtin binary operation expression such as "x + y" or "x <= y".
Expr * IgnoreParenCasts() LLVM_READONLY
IgnoreParenCasts - Ignore parentheses and casts.
Represents an Objective-C protocol declaration.
#define REGISTER_CHECKER(name)
Represents an ObjC class declaration.
virtual Decl * getCanonicalDecl()
Retrieves the "canonical" declaration of the given declaration.
ObjCMethodDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
bool isObjCSelfExpr() const
Check if this expression is the ObjC 'self' implicit parameter.
Expr - This represents one expression.
An expression that sends a message to the given Objective-C object or class.
bool isInstanceMethod() const
Selector getSelector() const
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
BugReporter is a utility class for generating PathDiagnostics for analysis.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
OpaqueValueExpr - An expression referring to an opaque object of a fixed type and value class...
unsigned getNumArgs() const
Return the number of actual arguments in this message, not counting the receiver. ...
PseudoObjectExpr - An expression which accesses a pseudo-object l-value.
bool isImplicitProperty() const
NullPointerConstantKind isNullPointerConstant(ASTContext &Ctx, NullPointerConstantValueDependence NPC) const
isNullPointerConstant - C99 6.3.2.3p3 - Test if this reduces down to a Null pointer constant...
bool getSynthesize() const
Expression is not a Null pointer constant.
Stmt * getBody() const override
Retrieve the body of this method, if it has one.
static PathDiagnosticLocation createEnd(const Stmt *S, const SourceManager &SM, const LocationOrAnalysisDeclContext LAC)
Create a location for the end of the statement.
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.
const ObjCMethodDecl * getMethodDecl() const
StringRef getName() const
Return the actual identifier string.
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
ObjCIvarDecl * getNextIvar()
const ObjCInterfaceDecl * getClassInterface() const
Dataflow Directional Tag Classes.
const ObjCInterfaceDecl * getContainingInterface() const
Return the class interface that this ivar is logically contained in; this is either the interface whe...
llvm::DenseMap< std::pair< IdentifierInfo *, unsigned >, ObjCPropertyDecl * > PropertyMap
Represents a pointer to an Objective C object.
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface...
bool hasBody() const override
Determine whether this method has a body.
ObjCIvarRefExpr - A reference to an ObjC instance variable.
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
ObjCIvarDecl - Represents an ObjC instance variable.
ObjCIvarDecl * getPropertyIvarDecl() const
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
ObjCMethodDecl * getMethod(Selector Sel, bool isInstance, bool AllowHidden=false) const
ObjCMethodDecl * getGetterMethodDecl() const
ObjCIvarDecl * all_declared_ivar_begin()
all_declared_ivar_begin - return first ivar declared in this class, its extensions and its implementa...