37#include "llvm/ADT/DenseMap.h"
38#include "llvm/ADT/STLExtras.h"
39#include "llvm/ADT/SetVector.h"
40#include "llvm/ADT/SmallString.h"
48 bool check_MissingInvalidationMethod =
false;
50 bool check_InstanceVariableInvalidation =
false;
56class IvarInvalidationCheckerImpl {
65 struct InvalidationInfo {
67 bool IsInvalidated =
false;
70 MethodSet InvalidationMethods;
72 InvalidationInfo() =
default;
74 InvalidationMethods.insert(MD);
77 bool needsInvalidation()
const {
78 return !InvalidationMethods.empty();
94 typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
104 bool &CalledAnotherInvalidationMethod;
107 const MethToIvarMapTy &PropertySetterToIvarMap;
110 const MethToIvarMapTy &PropertyGetterToIvarMap;
113 const PropToIvarMapTy &PropertyToIvarMap;
124 bool isZero(
const Expr *
E)
const;
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,
194 IvarSet &TrackedIvars,
198 static void printIvar(llvm::raw_svector_ostream &os,
200 const IvarToPropMapTy &IvarToPopertyMap);
204 const IvarToPropMapTy &IvarToPopertyMap,
206 bool MissingDeclaration)
const;
208 void reportIvarNeedsInvalidation(
const ObjCIvarDecl *IvarD,
209 const IvarToPropMapTy &IvarToPopertyMap,
215 const ChecksFilter &
Filter;
220 const ChecksFilter &InFilter) :
221 Mgr (InMgr), BR(InBR),
Filter(InFilter) {}
226static 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")
238void 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);
278bool IvarInvalidationCheckerImpl::trackIvar(
const ObjCIvarDecl *Iv,
279 IvarSet &TrackedIvars,
287 InvalidationInfo Info;
288 containsInvalidationMethod(IvInterf, Info,
false);
289 if (Info.needsInvalidation()) {
291 TrackedIvars[I] = Info;
299const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
302 IvarSet &TrackedIvars,
311 if (TrackedIvars.count(IvarD)) {
315 if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
321 for (
const ObjCIvarDecl *Iv : llvm::make_first_range(TrackedIvars)) {
322 StringRef IvarName = Iv->
getName();
324 if (IvarName == PropName)
329 llvm::raw_svector_ostream os(PropNameWithUnderscore);
330 os <<
'_' << PropName;
332 if (IvarName == PropNameWithUnderscore)
342void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
344 const IvarToPropMapTy &IvarToPopertyMap) {
347 assert(PD &&
"Do we synthesize ivars for something other than properties?");
348 os <<
"Property "<< PD->
getName() <<
" ";
350 os <<
"Instance variable "<< IvarDecl->
getName() <<
" ";
356void IvarInvalidationCheckerImpl::
370 trackIvar(Iv, Ivars, &FirstIvarDecl);
374 MethToIvarMapTy PropSetterToIvarMap;
375 MethToIvarMapTy PropGetterToIvarMap;
376 PropToIvarMapTy PropertyToIvarMap;
377 IvarToPropMapTy IvarToPopertyMap;
386 const ObjCIvarDecl *
ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
393 PropertyToIvarMap[PD] =
ID;
394 IvarToPopertyMap[
ID] = PD;
400 PropSetterToIvarMap[SetterD] =
ID;
406 PropGetterToIvarMap[GetterD] =
ID;
415 InvalidationInfo PartialInfo;
416 containsInvalidationMethod(InterfaceD, PartialInfo,
true);
420 bool AtImplementationContainsAtLeastOnePartialInvalidationMethod =
false;
421 for (
const ObjCMethodDecl *InterfD : PartialInfo.InvalidationMethods) {
426 AtImplementationContainsAtLeastOnePartialInvalidationMethod =
true;
428 bool CalledAnotherInvalidationMethod =
false;
431 CalledAnotherInvalidationMethod,
435 BR.getContext()).VisitStmt(
D->
getBody());
438 if (CalledAnotherInvalidationMethod)
449 InvalidationInfo Info;
450 containsInvalidationMethod(InterfaceD, Info,
false);
453 if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) {
454 if (
Filter.check_MissingInvalidationMethod)
455 reportNoInvalidationMethod(
Filter.checkName_MissingInvalidationMethod,
456 FirstIvarDecl, IvarToPopertyMap, InterfaceD,
465 if (!
Filter.check_InstanceVariableInvalidation)
469 bool AtImplementationContainsAtLeastOneInvalidationMethod =
false;
475 AtImplementationContainsAtLeastOneInvalidationMethod =
true;
478 IvarSet IvarsI = Ivars;
480 bool CalledAnotherInvalidationMethod =
false;
481 MethodCrawler(IvarsI,
482 CalledAnotherInvalidationMethod,
486 BR.getContext()).VisitStmt(
D->
getBody());
489 if (CalledAnotherInvalidationMethod)
493 for (
const ObjCIvarDecl *Ivar : llvm::make_first_range(IvarsI))
494 reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap,
D);
499 if (!AtImplementationContainsAtLeastOneInvalidationMethod) {
500 if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
503 for (
const ObjCIvarDecl *Ivar : llvm::make_first_range(Ivars))
504 reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap,
nullptr);
507 reportNoInvalidationMethod(
Filter.checkName_InstanceVariableInvalidation,
508 FirstIvarDecl, IvarToPopertyMap, InterfaceD,
514void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
516 const IvarToPropMapTy &IvarToPopertyMap,
519 llvm::raw_svector_ostream os(sbuf);
520 assert(FirstIvarDecl);
521 printIvar(os, FirstIvarDecl, IvarToPopertyMap);
522 os <<
"needs to be invalidated; ";
523 if (MissingDeclaration)
524 os <<
"no invalidation method is declared for ";
526 os <<
"no invalidation method is defined in the @implementation for ";
532 BR.EmitBasicReport(FirstIvarDecl, CheckName,
"Incomplete invalidation",
537void IvarInvalidationCheckerImpl::
539 const IvarToPropMapTy &IvarToPopertyMap,
542 llvm::raw_svector_ostream os(sbuf);
543 printIvar(os, IvarD, IvarToPopertyMap);
544 os <<
"needs to be invalidated or set to nil";
548 BR.getSourceManager(),
549 Mgr.getAnalysisDeclContext(MethodD));
550 BR.EmitBasicReport(MethodD,
Filter.checkName_InstanceVariableInvalidation,
551 "Incomplete invalidation",
556 IvarD,
Filter.checkName_InstanceVariableInvalidation,
563void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
565 IvarSet::iterator I = IVars.find(Iv);
566 if (I != IVars.end()) {
570 if (!InvalidationMethod || I->second.hasMethod(InvalidationMethod))
575const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(
const Expr *
E)
const {
584void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
590void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
595 MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
596 if (IvI != PropertyGetterToIvarMap.end())
597 markInvalidated(IvI->second);
601void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
608 PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
609 if (IvI != PropertyToIvarMap.end())
610 markInvalidated(IvI->second);
619 MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
620 if (IvI != PropertyGetterToIvarMap.end())
621 markInvalidated(IvI->second);
627bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(
const Expr *
E)
const {
634void IvarInvalidationCheckerImpl::MethodCrawler::check(
const Expr *
E) {
638 checkObjCIvarRefExpr(IvarRef);
643 checkObjCPropertyRefExpr(PropRef);
648 checkObjCMessageExpr(MsgExpr);
653void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
660 if (Opcode != BO_Assign &&
665 if (isZero(BO->
getRHS())) {
670 if (Opcode != BO_Assign && isZero(BO->
getLHS())) {
676void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
682 if (Receiver && isInvalidationMethod(MD,
false))
684 CalledAnotherInvalidationMethod =
true;
691 MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
692 if (IvI != PropertySetterToIvarMap.end()) {
693 markInvalidated(IvI->second);
700 InvalidationMethod = MD;
702 InvalidationMethod =
nullptr;
711class IvarInvalidationChecker :
712 public Checker<check::ASTDecl<ObjCImplementationDecl> > {
718 IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
728bool ento::shouldRegisterIvarInvalidationModeling(
const CheckerManager &mgr) {
732#define REGISTER_CHECKER(name) \
733 void ento::register##name(CheckerManager &mgr) { \
734 IvarInvalidationChecker *checker = \
735 mgr.getChecker<IvarInvalidationChecker>(); \
736 checker->Filter.check_##name = true; \
737 checker->Filter.checkName_##name = mgr.getCurrentCheckerName(); \
740 bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
#define REGISTER_CHECKER(name)
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
A builtin binary operation expression such as "x + y" or "x <= y".
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
Decl - This represents one declaration (or definition), e.g.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
virtual bool hasBody() const
Returns true if this Decl represents a declaration for a body of code, such as a function or method d...
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
virtual Decl * getCanonicalDecl()
Retrieves the "canonical" declaration of the given declaration.
This represents one expression.
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
bool isObjCSelfExpr() const
Check if this expression is the ObjC 'self' implicit parameter.
@ NPC_ValueDependentIsNotNull
Specifies that a value-dependent expression should be considered to never be a null pointer constant.
@ NPCK_NotNull
Expression is not a Null pointer constant.
NullPointerConstantKind isNullPointerConstant(ASTContext &Ctx, NullPointerConstantValueDependence NPC) const
isNullPointerConstant - C99 6.3.2.3p3 - Test if this reduces down to a Null pointer constant.
StringRef getName() const
Return the actual identifier string.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
ObjCContainerDecl - Represents a container for method declarations.
ObjCMethodDecl * getMethod(Selector Sel, bool isInstance, bool AllowHidden=false) const
llvm::MapVector< std::pair< IdentifierInfo *, unsigned >, ObjCPropertyDecl * > PropertyMap
const ObjCInterfaceDecl * getClassInterface() const
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Represents an ObjC class declaration.
ObjCIvarDecl * all_declared_ivar_begin()
all_declared_ivar_begin - return first ivar declared in this class, its extensions and its implementa...
void collectPropertiesToImplement(PropertyMap &PM) const override
This routine collects list of properties to be implemented in the class.
ObjCIvarDecl - Represents an ObjC instance variable.
bool getSynthesize() const
ObjCInterfaceDecl * getContainingInterface()
Return the class interface that this ivar is logically contained in; this is either the interface whe...
ObjCIvarDecl * getNextIvar()
ObjCIvarDecl * getCanonicalDecl() override
Retrieves the canonical declaration of this field.
ObjCIvarRefExpr - A reference to an ObjC instance variable.
An expression that sends a message to the given Objective-C object or class.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
const ObjCMethodDecl * getMethodDecl() const
unsigned getNumArgs() const
Return the number of actual arguments in this message, not counting the receiver.
ObjCMethodDecl - Represents an instance or class method declaration.
Stmt * getBody() const override
Retrieve the body of this method, if it has one.
ObjCMethodDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Selector getSelector() const
bool isInstanceMethod() const
Represents a pointer to an Objective C object.
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface.
Represents one property declaration in an Objective-C interface.
bool isClassProperty() const
ObjCMethodDecl * getGetterMethodDecl() const
ObjCMethodDecl * getSetterMethodDecl() const
ObjCIvarDecl * getPropertyIvarDecl() const
ObjCPropertyRefExpr - A dot-syntax expression to access an ObjC property.
ObjCPropertyDecl * getExplicitProperty() const
bool isExplicitProperty() const
bool isImplicitProperty() const
ObjCMethodDecl * getImplicitPropertySetter() const
Represents an Objective-C protocol declaration.
OpaqueValueExpr - An expression referring to an opaque object of a fixed type and value class.
PseudoObjectExpr - An expression which accesses a pseudo-object l-value.
A (possibly-)qualified type.
Stmt - This represents one statement.
const T * getAs() const
Member-template getAs<specific type>'.
BugReporter is a utility class for generating PathDiagnostics for analysis.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
This wrapper is used to ensure that only StringRefs originating from the CheckerRegistry are used as ...
static PathDiagnosticLocation createEnd(const Stmt *S, const SourceManager &SM, const LocationOrAnalysisDeclContext LAC)
Create a location for the end of the statement.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
const char *const CoreFoundationObjectiveC
The JSON file list parser is used to communicate input to InstallAPI.