| // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.SuperDealloc,debug.ExprInspection -analyzer-output=text -verify %s |
| |
| void clang_analyzer_warnIfReached(); |
| |
| #define nil ((id)0) |
| |
| typedef unsigned long NSUInteger; |
| @protocol NSObject |
| - (instancetype)retain; |
| - (oneway void)release; |
| @end |
| |
| @interface NSObject <NSObject> { } |
| - (void)dealloc; |
| - (instancetype)init; |
| @end |
| |
| typedef struct objc_selector *SEL; |
| |
| //===------------------------------------------------------------------------=== |
| // <rdar://problem/6953275> |
| // Check that 'self' is not referenced after calling '[super dealloc]'. |
| |
| @interface SuperDeallocThenReleaseIvarClass : NSObject { |
| NSObject *_ivar; |
| } |
| @end |
| |
| @implementation SuperDeallocThenReleaseIvarClass |
| - (instancetype)initWithIvar:(NSObject *)ivar { |
| self = [super init]; |
| if (!self) |
| return nil; |
| _ivar = [ivar retain]; |
| return self; |
| } |
| - (void)dealloc { |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| [_ivar release]; // expected-warning {{Use of instance variable '_ivar' after 'self' has been deallocated}} |
| // expected-note@-1 {{Use of instance variable '_ivar' after 'self' has been deallocated}} |
| } |
| @end |
| |
| @interface SuperDeallocThenAssignNilToIvarClass : NSObject { |
| NSObject *_delegate; |
| } |
| @end |
| |
| @implementation SuperDeallocThenAssignNilToIvarClass |
| - (instancetype)initWithDelegate:(NSObject *)delegate { |
| self = [super init]; |
| if (!self) |
| return nil; |
| _delegate = delegate; |
| return self; |
| } |
| - (void)dealloc { |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| _delegate = nil; // expected-warning {{Use of instance variable '_delegate' after 'self' has been deallocated}} |
| // expected-note@-1 {{Use of instance variable '_delegate' after 'self' has been deallocated}} |
| } |
| @end |
| |
| |
| struct SomeStruct { |
| int f; |
| }; |
| |
| @interface SuperDeallocThenAssignIvarField : NSObject { |
| struct SomeStruct _s; |
| } |
| @end |
| |
| @implementation SuperDeallocThenAssignIvarField |
| - (void)dealloc { |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| _s.f = 7; // expected-warning {{Use of instance variable '_s' after 'self' has been deallocated}} |
| // expected-note@-1 {{Use of instance variable '_s' after 'self' has been deallocated}} |
| } |
| @end |
| |
| @interface OtherClassWithIvar { |
| @public |
| int _otherIvar; |
| } |
| @end; |
| |
| @interface SuperDeallocThenAssignIvarIvar : NSObject { |
| OtherClassWithIvar *_ivar; |
| } |
| @end |
| |
| @implementation SuperDeallocThenAssignIvarIvar |
| - (void)dealloc { |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| _ivar->_otherIvar = 7; // expected-warning {{Use of instance variable '_ivar' after 'self' has been deallocated}} |
| // expected-note@-1 {{Use of instance variable '_ivar' after 'self' has been deallocated}} |
| } |
| @end |
| |
| @interface SuperDeallocThenAssignSelfIvar : NSObject { |
| NSObject *_ivar; |
| } |
| @end |
| |
| @implementation SuperDeallocThenAssignSelfIvar |
| - (void)dealloc { |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| self->_ivar = nil; // expected-warning {{Use of instance variable '_ivar' after 'self' has been deallocated}} |
| // expected-note@-1 {{Use of instance variable '_ivar' after 'self' has been deallocated}} |
| } |
| @end |
| |
| @interface SuperDeallocThenReleasePropertyClass : NSObject { } |
| @property (retain) NSObject *ivar; |
| @end |
| |
| @implementation SuperDeallocThenReleasePropertyClass |
| - (instancetype)initWithProperty:(NSObject *)ivar { |
| self = [super init]; |
| if (!self) |
| return nil; |
| self.ivar = ivar; |
| return self; |
| } |
| - (void)dealloc { |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| self.ivar = nil; // expected-warning {{Use of 'self' after it has been deallocated}} |
| // expected-note@-1 {{Use of 'self' after it has been deallocated}} |
| } |
| @end |
| |
| @interface SuperDeallocThenAssignNilToPropertyClass : NSObject { } |
| @property (assign) NSObject *delegate; |
| @end |
| |
| @implementation SuperDeallocThenAssignNilToPropertyClass |
| - (instancetype)initWithDelegate:(NSObject *)delegate { |
| self = [super init]; |
| if (!self) |
| return nil; |
| self.delegate = delegate; |
| return self; |
| } |
| - (void)dealloc { |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| self.delegate = nil; // expected-warning {{Use of 'self' after it has been deallocated}} |
| // expected-note@-1 {{Use of 'self' after it has been deallocated}} |
| } |
| @end |
| |
| @interface SuperDeallocThenCallInstanceMethodClass : NSObject { } |
| - (void)_invalidate; |
| @end |
| |
| @implementation SuperDeallocThenCallInstanceMethodClass |
| - (void)_invalidate { |
| } |
| - (void)dealloc { |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| [self _invalidate]; // expected-warning {{Use of 'self' after it has been deallocated}} |
| // expected-note@-1 {{Use of 'self' after it has been deallocated}} |
| } |
| @end |
| |
| @interface SuperDeallocThenCallNonObjectiveCMethodClass : NSObject { } |
| @end |
| |
| static void _invalidate(NSObject *object) { |
| (void)object; |
| } |
| |
| @implementation SuperDeallocThenCallNonObjectiveCMethodClass |
| - (void)dealloc { |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| _invalidate(self); // expected-warning {{Use of 'self' after it has been deallocated}} |
| // expected-note@-1 {{Use of 'self' after it has been deallocated}} |
| } |
| @end |
| |
| @interface SuperDeallocThenCallObjectiveClassMethodClass : NSObject { } |
| @end |
| |
| @implementation SuperDeallocThenCallObjectiveClassMethodClass |
| + (void) invalidate:(id)arg; { |
| } |
| |
| - (void)dealloc { |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| [SuperDeallocThenCallObjectiveClassMethodClass invalidate:self]; // expected-warning {{Use of 'self' after it has been deallocated}} |
| // expected-note@-1 {{Use of 'self' after it has been deallocated}} |
| } |
| @end |
| |
| @interface TwoSuperDeallocCallsClass : NSObject { |
| NSObject *_ivar; |
| } |
| - (void)_invalidate; |
| @end |
| |
| @implementation TwoSuperDeallocCallsClass |
| - (void)_invalidate { |
| } |
| - (void)dealloc { |
| if (_ivar) { // expected-note {{Assuming the condition is false}} expected-note {{Taking false branch}} |
| [_ivar release]; |
| [super dealloc]; |
| return; |
| } |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| [self _invalidate]; // expected-warning {{Use of 'self' after it has been deallocated}} |
| // expected-note@-1 {{Use of 'self' after it has been deallocated}} |
| } |
| @end |
| |
| //===------------------------------------------------------------------------=== |
| // Warn about calling [super dealloc] twice due to missing return statement. |
| |
| @interface MissingReturnCausesDoubleSuperDeallocClass : NSObject { |
| NSObject *_ivar; |
| } |
| @end |
| |
| @implementation MissingReturnCausesDoubleSuperDeallocClass |
| - (void)dealloc { |
| if (_ivar) { // expected-note {{Assuming the condition is true}} expected-note {{Taking true branch}} |
| [_ivar release]; |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| // return; |
| } |
| [super dealloc]; // expected-warning{{[super dealloc] should not be called multiple times}} |
| // expected-note@-1{{[super dealloc] should not be called multiple times}} |
| } |
| @end |
| |
| //===------------------------------------------------------------------------=== |
| // Warn about calling [super dealloc] twice in two different methods. |
| |
| @interface SuperDeallocInOtherMethodClass : NSObject { |
| NSObject *_ivar; |
| } |
| - (void)_cleanup; |
| @end |
| |
| @implementation SuperDeallocInOtherMethodClass |
| - (void)_cleanup { |
| [_ivar release]; |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| } |
| - (void)dealloc { |
| [self _cleanup]; // expected-note {{Calling '_cleanup'}} |
| //expected-note@-1 {{Returning from '_cleanup'}} |
| [super dealloc]; // expected-warning {{[super dealloc] should not be called multiple times}} |
| // expected-note@-1 {{[super dealloc] should not be called multiple times}} |
| } |
| @end |
| |
| //===------------------------------------------------------------------------=== |
| // Do not warn about calling [super dealloc] recursively for different objects |
| // of the same type with custom retain counting. |
| // |
| // A class that contains an ivar of itself with custom retain counting (such |
| // as provided by _OBJC_SUPPORTED_INLINE_REFCNT_WITH_DEALLOC2MAIN) can generate |
| // a false positive that [super dealloc] is called twice if each object instance |
| // is not tracked separately by the checker. This test case is just a simple |
| // approximation to trigger the false positive. |
| |
| @class ClassWithOwnIvarInstanceClass; |
| @interface ClassWithOwnIvarInstanceClass : NSObject { |
| ClassWithOwnIvarInstanceClass *_ivar; |
| NSUInteger _retainCount; |
| } |
| @end |
| |
| @implementation ClassWithOwnIvarInstanceClass |
| - (instancetype)retain { |
| ++_retainCount; |
| return self; |
| } |
| - (oneway void)release { |
| --_retainCount; |
| if (!_retainCount) |
| [self dealloc]; |
| } |
| - (void)dealloc { |
| [_ivar release]; |
| [super dealloc]; // no warning: different instances of same class |
| } |
| @end |
| |
| //===------------------------------------------------------------------------=== |
| // Do not warn about calling [super dealloc] twice if +dealloc is a class |
| // method. |
| |
| @interface SuperDeallocClassMethodIgnoredClass : NSObject { } |
| + (void)dealloc; |
| @end |
| |
| @implementation SuperDeallocClassMethodIgnoredClass |
| + (void)dealloc { } |
| @end |
| |
| @interface SuperDeallocClassMethodIgnoredSubClass : NSObject { } |
| + (void)dealloc; |
| @end |
| |
| @implementation SuperDeallocClassMethodIgnoredSubClass |
| + (void)dealloc { |
| [super dealloc]; |
| [super dealloc]; // no warning: class method |
| } |
| @end |
| |
| //===------------------------------------------------------------------------=== |
| // Do not warn about calling [super dealloc] twice if when the analyzer has |
| // inlined the call to its super deallocator. |
| |
| @interface SuperClassCallingSuperDealloc : NSObject { |
| NSObject *_ivar; |
| } |
| @end |
| |
| @implementation SuperClassCallingSuperDealloc |
| - (void)dealloc; { |
| [_ivar release]; // no-warning |
| |
| [super dealloc]; |
| } |
| @end |
| |
| @interface SubclassCallingSuperDealloc : SuperClassCallingSuperDealloc |
| @end |
| |
| @implementation SubclassCallingSuperDealloc |
| - (void)dealloc; { |
| [super dealloc]; |
| } |
| @end |
| |
| //===------------------------------------------------------------------------=== |
| // Treat calling [super dealloc] twice as as a sink. |
| |
| @interface CallingSuperDeallocTwiceIsSink : NSObject |
| @end |
| |
| @implementation CallingSuperDeallocTwiceIsSink |
| - (void)dealloc; { |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| [super dealloc]; // expected-warning {{[super dealloc] should not be called multiple times}} |
| // expected-note@-1 {{[super dealloc] should not be called multiple times}} |
| |
| clang_analyzer_warnIfReached(); // no-warning |
| } |
| @end |
| |
| |
| //===------------------------------------------------------------------------=== |
| // Test path notes with intervening method call on self. |
| |
| @interface InterveningMethodCallOnSelf : NSObject |
| @end |
| |
| @implementation InterveningMethodCallOnSelf |
| - (void)anotherMethod { |
| } |
| |
| - (void)dealloc; { |
| [super dealloc]; // expected-note {{[super dealloc] called here}} |
| [self anotherMethod]; // expected-warning {{Use of 'self' after it has been deallocated}} |
| // expected-note@-1 {{Use of 'self' after it has been deallocated}} |
| [super dealloc]; |
| } |
| @end |