37 #include "llvm/ADT/DenseMap.h" 38 #include "llvm/ADT/SetVector.h" 39 #include "llvm/ADT/SmallString.h" 41 using namespace clang;
47 DefaultBool check_MissingInvalidationMethod;
49 DefaultBool check_InstanceVariableInvalidation;
51 CheckerNameRef checkName_MissingInvalidationMethod;
52 CheckerNameRef checkName_InstanceVariableInvalidation;
55 class IvarInvalidationCheckerImpl {
62 const ObjCPropertyDecl*> IvarToPropMapTy;
64 struct InvalidationInfo {
69 MethodSet InvalidationMethods;
71 InvalidationInfo() : IsInvalidated(
false) {}
72 void addInvalidationMethod(
const ObjCMethodDecl *MD) {
73 InvalidationMethods.insert(MD);
76 bool needsInvalidation()
const {
77 return !InvalidationMethods.empty();
80 bool hasMethod(
const ObjCMethodDecl *MD) {
83 for (MethodSet::iterator I = InvalidationMethods.begin(),
84 E = InvalidationMethods.end(); I != E; ++I) {
94 typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
104 bool &CalledAnotherInvalidationMethod;
107 const MethToIvarMapTy &PropertySetterToIvarMap;
110 const MethToIvarMapTy &PropertyGetterToIvarMap;
113 const PropToIvarMapTy &PropertyToIvarMap;
116 const ObjCMethodDecl *InvalidationMethod;
121 const Expr *peel(
const Expr *E)
const;
124 bool isZero(
const Expr *E)
const;
127 void markInvalidated(
const ObjCIvarDecl *Iv);
142 void check(
const Expr *E);
145 MethodCrawler(IvarSet &InIVars,
146 bool &InCalledAnotherInvalidationMethod,
147 const MethToIvarMapTy &InPropertySetterToIvarMap,
148 const MethToIvarMapTy &InPropertyGetterToIvarMap,
149 const PropToIvarMapTy &InPropertyToIvarMap,
152 CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
153 PropertySetterToIvarMap(InPropertySetterToIvarMap),
154 PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
155 PropertyToIvarMap(InPropertyToIvarMap),
156 InvalidationMethod(nullptr),
159 void VisitStmt(
const Stmt *S) { VisitChildren(S); }
165 void VisitChildren(
const Stmt *S) {
166 for (
const auto *Child : S->
children()) {
169 if (CalledAnotherInvalidationMethod)
180 InvalidationInfo &Out,
181 bool LookForPartial);
185 static bool trackIvar(
const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
186 const ObjCIvarDecl **FirstIvarDecl);
191 static const ObjCIvarDecl *findPropertyBackingIvar(
192 const ObjCPropertyDecl *Prop,
194 IvarSet &TrackedIvars,
195 const ObjCIvarDecl **FirstIvarDecl);
198 static void printIvar(llvm::raw_svector_ostream &os,
199 const ObjCIvarDecl *IvarDecl,
200 const IvarToPropMapTy &IvarToPopertyMap);
202 void reportNoInvalidationMethod(CheckerNameRef CheckName,
203 const ObjCIvarDecl *FirstIvarDecl,
204 const IvarToPropMapTy &IvarToPopertyMap,
206 bool MissingDeclaration)
const;
208 void reportIvarNeedsInvalidation(
const ObjCIvarDecl *IvarD,
209 const IvarToPropMapTy &IvarToPopertyMap,
210 const ObjCMethodDecl *MethodD)
const;
212 AnalysisManager& Mgr;
215 const ChecksFilter &
Filter;
218 IvarInvalidationCheckerImpl(AnalysisManager& InMgr,
220 const ChecksFilter &InFilter) :
221 Mgr (InMgr), BR(InBR), Filter(InFilter) {}
226 static bool isInvalidationMethod(
const ObjCMethodDecl *M,
bool LookForPartial) {
228 if (!LookForPartial &&
229 Ann->getAnnotation() ==
"objc_instance_variable_invalidator")
231 if (LookForPartial &&
232 Ann->getAnnotation() ==
"objc_instance_variable_invalidator_partial")
238 void IvarInvalidationCheckerImpl::containsInvalidationMethod(
244 assert(!isa<ObjCImplementationDecl>(D));
248 for (
const auto *MDI : D->
methods())
249 if (isInvalidationMethod(MDI, Partial))
250 OutInfo.addInvalidationMethod(
251 cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
257 for (
const auto *I : InterfD->protocols())
258 containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
262 for (
const auto *Ext : InterfD->visible_extensions())
263 containsInvalidationMethod(Ext, OutInfo, Partial);
265 containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
271 for (
const auto *I : ProtD->protocols()) {
272 containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
278 bool IvarInvalidationCheckerImpl::trackIvar(
const ObjCIvarDecl *Iv,
279 IvarSet &TrackedIvars,
280 const ObjCIvarDecl **FirstIvarDecl) {
287 InvalidationInfo Info;
288 containsInvalidationMethod(IvInterf, Info,
false);
289 if (Info.needsInvalidation()) {
291 TrackedIvars[I] = Info;
299 const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
300 const ObjCPropertyDecl *Prop,
302 IvarSet &TrackedIvars,
303 const ObjCIvarDecl **FirstIvarDecl) {
304 const ObjCIvarDecl *IvarD =
nullptr;
311 if (TrackedIvars.count(IvarD)) {
315 if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
321 for (IvarSet::const_iterator I = TrackedIvars.begin(),
322 E = TrackedIvars.end(); I != E; ++I) {
323 const ObjCIvarDecl *Iv = I->first;
324 StringRef IvarName = Iv->
getName();
326 if (IvarName == PropName)
331 llvm::raw_svector_ostream os(PropNameWithUnderscore);
332 os <<
'_' << PropName;
334 if (IvarName == PropNameWithUnderscore)
344 void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
345 const ObjCIvarDecl *IvarDecl,
346 const IvarToPropMapTy &IvarToPopertyMap) {
348 const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
349 assert(PD &&
"Do we synthesize ivars for something other than properties?");
350 os <<
"Property "<< PD->
getName() <<
" ";
352 os <<
"Instance variable "<< IvarDecl->
getName() <<
" ";
358 void IvarInvalidationCheckerImpl::
365 const ObjCIvarDecl *FirstIvarDecl =
nullptr;
372 trackIvar(Iv, Ivars, &FirstIvarDecl);
376 MethToIvarMapTy PropSetterToIvarMap;
377 MethToIvarMapTy PropGetterToIvarMap;
378 PropToIvarMapTy PropertyToIvarMap;
379 IvarToPropMapTy IvarToPopertyMap;
385 for (ObjCInterfaceDecl::PropertyMap::iterator
386 I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
387 const ObjCPropertyDecl *PD = I->second;
391 const ObjCIvarDecl *
ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
398 PropertyToIvarMap[PD] =
ID;
399 IvarToPopertyMap[
ID] = PD;
405 PropSetterToIvarMap[SetterD] =
ID;
411 PropGetterToIvarMap[GetterD] =
ID;
420 InvalidationInfo PartialInfo;
421 containsInvalidationMethod(InterfaceD, PartialInfo,
true);
425 bool AtImplementationContainsAtLeastOnePartialInvalidationMethod =
false;
426 for (MethodSet::iterator
427 I = PartialInfo.InvalidationMethods.begin(),
428 E = PartialInfo.InvalidationMethods.end(); I != E; ++I) {
429 const ObjCMethodDecl *InterfD = *I;
435 AtImplementationContainsAtLeastOnePartialInvalidationMethod =
true;
437 bool CalledAnotherInvalidationMethod =
false;
440 CalledAnotherInvalidationMethod,
444 BR.getContext()).VisitStmt(D->
getBody());
447 if (CalledAnotherInvalidationMethod)
458 InvalidationInfo Info;
459 containsInvalidationMethod(InterfaceD, Info,
false);
462 if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) {
463 if (
Filter.check_MissingInvalidationMethod)
464 reportNoInvalidationMethod(
Filter.checkName_MissingInvalidationMethod,
465 FirstIvarDecl, IvarToPopertyMap, InterfaceD,
474 if (!
Filter.check_InstanceVariableInvalidation)
478 bool AtImplementationContainsAtLeastOneInvalidationMethod =
false;
479 for (MethodSet::iterator I = Info.InvalidationMethods.begin(),
480 E = Info.InvalidationMethods.end(); I != E; ++I) {
481 const ObjCMethodDecl *InterfD = *I;
487 AtImplementationContainsAtLeastOneInvalidationMethod =
true;
490 IvarSet IvarsI = Ivars;
492 bool CalledAnotherInvalidationMethod =
false;
493 MethodCrawler(IvarsI,
494 CalledAnotherInvalidationMethod,
498 BR.getContext()).VisitStmt(D->
getBody());
501 if (CalledAnotherInvalidationMethod)
505 for (IvarSet::const_iterator
506 I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I)
507 reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D);
512 if (!AtImplementationContainsAtLeastOneInvalidationMethod) {
513 if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
516 for (IvarSet::const_iterator
517 I = Ivars.begin(), E = Ivars.end(); I != E; ++I)
518 reportIvarNeedsInvalidation(I->first, IvarToPopertyMap,
nullptr);
521 reportNoInvalidationMethod(
Filter.checkName_InstanceVariableInvalidation,
522 FirstIvarDecl, IvarToPopertyMap, InterfaceD,
528 void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
529 CheckerNameRef CheckName,
const ObjCIvarDecl *FirstIvarDecl,
530 const IvarToPropMapTy &IvarToPopertyMap,
533 llvm::raw_svector_ostream os(sbuf);
534 assert(FirstIvarDecl);
535 printIvar(os, FirstIvarDecl, IvarToPopertyMap);
536 os <<
"needs to be invalidated; ";
537 if (MissingDeclaration)
538 os <<
"no invalidation method is declared for ";
540 os <<
"no invalidation method is defined in the @implementation for ";
543 PathDiagnosticLocation IvarDecLocation =
546 BR.EmitBasicReport(FirstIvarDecl, CheckName,
"Incomplete invalidation",
551 void IvarInvalidationCheckerImpl::
552 reportIvarNeedsInvalidation(
const ObjCIvarDecl *IvarD,
553 const IvarToPropMapTy &IvarToPopertyMap,
554 const ObjCMethodDecl *MethodD)
const {
556 llvm::raw_svector_ostream os(sbuf);
557 printIvar(os, IvarD, IvarToPopertyMap);
558 os <<
"needs to be invalidated or set to nil";
560 PathDiagnosticLocation MethodDecLocation =
562 BR.getSourceManager(),
563 Mgr.getAnalysisDeclContext(MethodD));
564 BR.EmitBasicReport(MethodD,
Filter.checkName_InstanceVariableInvalidation,
565 "Incomplete invalidation",
570 IvarD,
Filter.checkName_InstanceVariableInvalidation,
577 void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
578 const ObjCIvarDecl *Iv) {
579 IvarSet::iterator I = IVars.find(Iv);
580 if (I != IVars.end()) {
584 if (!InvalidationMethod || I->second.hasMethod(InvalidationMethod))
589 const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(
const Expr *E)
const {
598 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
604 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
609 MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
610 if (IvI != PropertyGetterToIvarMap.end())
611 markInvalidated(IvI->second);
615 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
622 PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
623 if (IvI != PropertyToIvarMap.end())
624 markInvalidated(IvI->second);
633 MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
634 if (IvI != PropertyGetterToIvarMap.end())
635 markInvalidated(IvI->second);
641 bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(
const Expr *E)
const {
648 void IvarInvalidationCheckerImpl::MethodCrawler::check(
const Expr *E) {
652 checkObjCIvarRefExpr(IvarRef);
657 checkObjCPropertyRefExpr(PropRef);
662 checkObjCMessageExpr(MsgExpr);
667 void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
674 if (Opcode != BO_Assign &&
679 if (isZero(BO->
getRHS())) {
684 if (Opcode != BO_Assign && isZero(BO->
getLHS())) {
690 void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
696 if (Receiver && isInvalidationMethod(MD,
false))
698 CalledAnotherInvalidationMethod =
true;
705 MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
706 if (IvI != PropertySetterToIvarMap.end()) {
707 markInvalidated(IvI->second);
714 InvalidationMethod = MD;
716 InvalidationMethod =
nullptr;
725 class IvarInvalidationChecker :
726 public Checker<check::ASTDecl<ObjCImplementationDecl> > {
731 BugReporter &BR)
const {
732 IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
738 void ento::registerIvarInvalidationModeling(CheckerManager &mgr) {
739 mgr.registerChecker<IvarInvalidationChecker>();
742 bool ento::shouldRegisterIvarInvalidationModeling(
const LangOptions &LO) {
746 #define REGISTER_CHECKER(name) \ 747 void ento::register##name(CheckerManager &mgr) { \ 748 IvarInvalidationChecker *checker = \ 749 mgr.getChecker<IvarInvalidationChecker>(); \ 750 checker->Filter.check_##name = true; \ 751 checker->Filter.checkName_##name = mgr.getCurrentCheckerName(); \ 754 bool ento::shouldRegister##name(const LangOptions &LO) { return true; } 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.
llvm::SmallVector< ObjCPropertyDecl *, 8 > PropertyDeclOrder
Decl - This represents one declaration (or definition), e.g.
ObjCMethodDecl * getImplicitPropertySetter() const
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
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
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
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
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.
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.
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...