aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnna Zaks <ganna@apple.com>2013-02-08 23:55:43 +0000
committerAnna Zaks <ganna@apple.com>2013-02-08 23:55:43 +0000
commit26db7dbf67b1532b2d617b3a85428699a1ffc997 (patch)
tree55ce87ed33d5391378539284bc7993983aab5844
parent125643455953069b925b30d2e4ae5674063984ed (diff)
[analyzer] IvarInvalidation: add annotation for partial invalidation
The new annotation allows having methods that only partially invalidate IVars and might not be called from the invalidation methods directly (instead, are guaranteed to be called before the invalidation occurs). The checker is going to trust the programmer to call the partial invalidation method before the invalidator.This is common in cases when partial object tear down happens before the death of the object. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@174779 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp117
-rw-r--r--test/Analysis/objc_invalidation.m78
2 files changed, 150 insertions, 45 deletions
diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
index 92e16f614d..1d7961ebea 100644
--- a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -20,6 +20,12 @@
// 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"
@@ -65,11 +71,7 @@ class IvarInvalidationChecker :
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(),
@@ -81,10 +83,6 @@ class IvarInvalidationChecker :
}
return false;
}
-
- bool isInvalidated() const {
- return IsInvalidated;
- }
};
typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
@@ -170,8 +168,11 @@ class IvarInvalidationChecker :
/// 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.
@@ -191,24 +192,29 @@ class IvarInvalidationChecker :
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;
@@ -221,7 +227,7 @@ void IvarInvalidationChecker::containsInvalidationMethod(
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()));
}
@@ -233,7 +239,7 @@ void IvarInvalidationChecker::containsInvalidationMethod(
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
@@ -242,10 +248,10 @@ void IvarInvalidationChecker::containsInvalidationMethod(
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;
}
@@ -254,7 +260,7 @@ void IvarInvalidationChecker::containsInvalidationMethod(
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;
}
@@ -272,7 +278,7 @@ bool IvarInvalidationChecker::trackIvar(const ObjCIvarDecl *Iv,
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;
@@ -402,9 +408,44 @@ void IvarInvalidationChecker::checkASTDecl(const ObjCImplementationDecl *ImplD,
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()) {
@@ -453,20 +494,19 @@ void IvarInvalidationChecker::checkASTDecl(const ObjCImplementationDecl *ImplD,
// 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);
+ }
}
}
@@ -496,10 +536,9 @@ void IvarInvalidationChecker::MethodCrawler::markInvalidated(
// 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);
}
}
@@ -610,7 +649,7 @@ void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr(
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;
diff --git a/test/Analysis/objc_invalidation.m b/test/Analysis/objc_invalidation.m
index 4fd46c2243..4b588ea36d 100644
--- a/test/Analysis/objc_invalidation.m
+++ b/test/Analysis/objc_invalidation.m
@@ -81,6 +81,11 @@ extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1,
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;
@@ -108,10 +113,15 @@ extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1,
-(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{
@@ -125,6 +135,7 @@ extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1,
@synthesize Prop5 = _Prop5;
@synthesize Prop4 = _Prop4;
@synthesize Prop8 = _Prop8;
+@synthesize Prop10 = _Prop10;
- (void) setProp1: (SomeInvalidationImplementingObject*) InObj {
@@ -161,13 +172,24 @@ extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1,
[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
@@ -252,3 +274,47 @@ extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1,
@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
+