// 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 addess 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 "ClangSACheckers.h"
return !InvalidationMethods.empty();
}
- void markInvalidated() {
- IsInvalidated = true;
- }
-
- bool markInvalidated(const ObjCMethodDecl *MD) {
+ bool hasMethod(const ObjCMethodDecl *MD) {
if (IsInvalidated)
return true;
for (MethodSet::iterator I = InvalidationMethods.begin(),
}
return false;
}
-
- bool isInvalidated() const {
- return IsInvalidated;
- }
};
typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
/// 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);
+ InvalidationInfo &Out,
+ bool LookForPartial);
/// Check if ivar should be tracked and add to TrackedIvars if positive.
/// Returns true if ivar should be tracked.
static void printIvar(llvm::raw_svector_ostream &os,
const ObjCIvarDecl *IvarDecl,
IvarToPropMapTy &IvarToPopertyMap);
+
public:
void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
BugReporter &BR) const;
};
-static bool isInvalidationMethod(const ObjCMethodDecl *M) {
+static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
for (specific_attr_iterator<AnnotateAttr>
AI = M->specific_attr_begin<AnnotateAttr>(),
AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) {
const AnnotateAttr *Ann = *AI;
- if (Ann->getAnnotation() == "objc_instance_variable_invalidator")
+ if (!LookForPartial &&
+ Ann->getAnnotation() == "objc_instance_variable_invalidator")
+ return true;
+ if (LookForPartial &&
+ Ann->getAnnotation() == "objc_instance_variable_invalidator_partial")
return true;
}
return false;
}
void IvarInvalidationChecker::containsInvalidationMethod(
- const ObjCContainerDecl *D, InvalidationInfo &OutInfo) {
+ const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
if (!D)
return;
I = D->meth_begin(),
E = D->meth_end(); I != E; ++I) {
const ObjCMethodDecl *MDI = *I;
- if (isInvalidationMethod(MDI))
+ if (isInvalidationMethod(MDI, Partial))
OutInfo.addInvalidationMethod(
cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
}
for (ObjCInterfaceDecl::protocol_iterator
I = InterfD->protocol_begin(),
E = InterfD->protocol_end(); I != E; ++I) {
- containsInvalidationMethod((*I)->getDefinition(), OutInfo);
+ containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial);
}
// Visit all categories in case the invalidation method is declared in
Ext = InterfD->visible_extensions_begin(),
ExtEnd = InterfD->visible_extensions_end();
Ext != ExtEnd; ++Ext) {
- containsInvalidationMethod(*Ext, OutInfo);
+ containsInvalidationMethod(*Ext, OutInfo, Partial);
}
- containsInvalidationMethod(InterfD->getSuperClass(), OutInfo);
+ containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
return;
}
for (ObjCInterfaceDecl::protocol_iterator
I = ProtD->protocol_begin(),
E = ProtD->protocol_end(); I != E; ++I) {
- containsInvalidationMethod((*I)->getDefinition(), OutInfo);
+ containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial);
}
return;
}
const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
InvalidationInfo Info;
- containsInvalidationMethod(IvInterf, Info);
+ containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
if (Info.needsInvalidation()) {
const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
TrackedIvars[I] = Info;
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.
+ 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()) {
+ 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);
+ containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
// Report an error in case none of the invalidation methods are declared.
if (!Info.needsInvalidation()) {
// Warn on the ivars that were not invalidated by the method.
for (IvarSet::const_iterator I = IvarsI.begin(),
- E = IvarsI.end(); I != E; ++I)
- if (!I->second.isInvalidated()) {
- SmallString<128> sbuf;
- llvm::raw_svector_ostream os(sbuf);
- printIvar(os, I->first, IvarToPopertyMap);
- os << "needs to be invalidated or set to nil";
- PathDiagnosticLocation MethodDecLocation =
- PathDiagnosticLocation::createEnd(D->getBody(),
- BR.getSourceManager(),
- Mgr.getAnalysisDeclContext(D));
- BR.EmitBasicReport(D, "Incomplete invalidation",
- categories::CoreFoundationObjectiveC, os.str(),
- MethodDecLocation);
- }
+ E = IvarsI.end(); I != E; ++I) {
+ SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ printIvar(os, I->first, IvarToPopertyMap);
+ os << "needs to be invalidated or set to nil";
+ PathDiagnosticLocation MethodDecLocation =
+ PathDiagnosticLocation::createEnd(D->getBody(),
+ BR.getSourceManager(),
+ Mgr.getAnalysisDeclContext(D));
+ BR.EmitBasicReport(D, "Incomplete invalidation",
+ categories::CoreFoundationObjectiveC, os.str(),
+ MethodDecLocation);
+ }
}
}
// 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.markInvalidated(InvalidationMethod);
- else
- I->second.markInvalidated();
+ if (!InvalidationMethod ||
+ (InvalidationMethod && I->second.hasMethod(InvalidationMethod)))
+ IVars.erase(I);
}
}
const Expr *Receiver = ME->getInstanceReceiver();
// Stop if we are calling '[self invalidate]'.
- if (Receiver && isInvalidationMethod(MD))
+ if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false))
if (Receiver->isObjCSelfExpr()) {
CalledAnotherInvalidationMethod = true;
return;
SomeInvalidationImplementingObject *_Prop5; // property with @synthesize, invalidate via getter method
SomeInvalidationImplementingObject *_Prop8;
+ // Ivars invalidated by the partial invalidator.
+ SomeInvalidationImplementingObject *Ivar9;
+ SomeInvalidationImplementingObject *_Prop10;
+ SomeInvalidationImplementingObject *Ivar11;
+
// No warnings on these as they are not invalidatable.
NSObject *NIvar1;
NSObject *NObj2;
-(void)invalidate;
+// Partial invalidators invalidate only some ivars. They are guaranteed to be
+// called before the invalidation methods.
+-(void)partialInvalidator1 __attribute__((annotate("objc_instance_variable_invalidator_partial")));
+-(void)partialInvalidator2 __attribute__((annotate("objc_instance_variable_invalidator_partial")));
@end
@interface SomeSubclassInvalidatableObject()
@property (assign) SomeInvalidationImplementingObject* Prop8;
+@property (assign) SomeInvalidationImplementingObject* Prop10;
@end
@implementation SomeSubclassInvalidatableObject{
@synthesize Prop5 = _Prop5;
@synthesize Prop4 = _Prop4;
@synthesize Prop8 = _Prop8;
+@synthesize Prop10 = _Prop10;
- (void) setProp1: (SomeInvalidationImplementingObject*) InObj {
[super invalidate];
}
// expected-warning@-1 {{Instance variable Ivar1 needs to be invalidated}}
- // expected-warning@-2 {{Instance variable MultipleProtocols needs to be invalidated}}
- // expected-warning@-3 {{Instance variable MultInheritance needs to be invalidated}}
- // expected-warning@-4 {{Property SynthIvarProp needs to be invalidated or set to nil}}
- // expected-warning@-5 {{Instance variable _Ivar3 needs to be invalidated}}
- // expected-warning@-6 {{Instance variable _Ivar4 needs to be invalidated}}
- // expected-warning@-7 {{Instance variable Ivar5 needs to be invalidated or set to nil}}
+// expected-warning@-2 {{Instance variable MultipleProtocols needs to be invalidated}}
+// expected-warning@-3 {{Instance variable MultInheritance needs to be invalidated}}
+// expected-warning@-4 {{Property SynthIvarProp needs to be invalidated or set to nil}}
+// expected-warning@-5 {{Instance variable _Ivar3 needs to be invalidated}}
+// expected-warning@-6 {{Instance variable _Ivar4 needs to be invalidated}}
+// expected-warning@-7 {{Instance variable Ivar5 needs to be invalidated or set to nil}}
// expected-warning@-8 {{Instance variable Ivar13 needs to be invalidated or set to nil}}
+
+
+-(void)partialInvalidator1 {
+ [Ivar9 invalidate];
+ [_Prop10 invalidate];
+}
+
+-(void)partialInvalidator2 {
+ [Ivar11 invalidate];
+}
+
@end
// Example, where the same property is inherited through
@end
@implementation MissingInvalidationMethodDecl2
@end
+
+@interface InvalidatedInPartial : SomeInvalidationImplementingObject {
+ SomeInvalidationImplementingObject *Ivar1;
+ SomeInvalidationImplementingObject *Ivar2;
+}
+-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
+@end
+@implementation InvalidatedInPartial
+-(void)partialInvalidator {
+ [Ivar1 invalidate];
+ Ivar2 = 0;
+}
+@end
+
+@interface NotInvalidatedInPartial : SomeInvalidationImplementingObject {
+ SomeInvalidationImplementingObject *Ivar1;
+}
+-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
+-(void)partialInvalidatorCallsPartial __attribute__((annotate("objc_instance_variable_invalidator_partial")));
+@end
+@implementation NotInvalidatedInPartial
+-(void)partialInvalidator {
+}
+-(void)partialInvalidatorCallsPartial {
+ [self partialInvalidator];
+}
+
+-(void)invalidate {
+} // expected-warning {{Instance variable Ivar1 needs to be invalidated or set to nil}}
+
+@end
+
+// False negative.
+@interface PartialCallsFull : SomeInvalidationImplementingObject {
+ SomeInvalidationImplementingObject *Ivar1;
+}
+-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
+@end
+@implementation PartialCallsFull
+-(void)partialInvalidator {
+ [self invalidate];
+} // TODO: It would be nice to check that the full invalidation method actually invalidates the ivar.
+@end
+