summaryrefslogtreecommitdiffstats
path: root/test/Analysis/retain-release-path-notes.m
blob: c3f5fcda44429ac1d559c54fa9f48a71acb575b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -analyzer-store=region -analyzer-output=text -verify %s

// This actually still works after the pseudo-object refactor, it just
// uses messages that say 'method' instead of 'property'.  Ted wanted
// this xfailed and filed as a bug.  rdar://problem/10402993

/***
This file is for testing the path-sensitive notes for retain/release errors.
Its goal is to have simple branch coverage of any path-based diagnostics,
not to actually check all possible retain/release errors.

This file includes notes that only appear in a ref-counted analysis. 
GC-specific notes should go in retain-release-path-notes-gc.m.
***/

@interface NSObject
+ (id)alloc;
- (id)init;
- (void)dealloc;

- (Class)class;

- (id)retain;
- (void)release;
- (void)autorelease;
@end

@interface Foo : NSObject
- (id)methodWithValue;
@property(retain) id propertyValue;
@end

typedef struct CFType *CFTypeRef;
CFTypeRef CFRetain(CFTypeRef);
void CFRelease(CFTypeRef);

id NSMakeCollectable(CFTypeRef);
CFTypeRef CFMakeCollectable(CFTypeRef);

CFTypeRef CFCreateSomething();
CFTypeRef CFGetSomething();


void creationViaAlloc () {
  id leaked = [[NSObject alloc] init]; // expected-warning{{leak}} expected-note{{Method returns an Objective-C object with a +1 retain count}}
  return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}}
}

void creationViaCFCreate () {
  CFTypeRef leaked = CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count}}
  return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}}
}

void acquisitionViaMethod (Foo *foo) {
  id leaked = [foo methodWithValue]; // expected-warning{{leak}} expected-note{{Method returns an Objective-C object with a +0 retain count}}
  [leaked retain]; // expected-note{{Reference count incremented. The object now has a +1 retain count}}
  [leaked retain]; // expected-note{{Reference count incremented. The object now has a +2 retain count}}
  [leaked release]; // expected-note{{Reference count decremented. The object now has a +1 retain count}}
  return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}}
}

void acquisitionViaProperty (Foo *foo) {
  id leaked = foo.propertyValue; // expected-warning{{leak}} expected-note{{Property returns an Objective-C object with a +0 retain count}}
  [leaked retain]; // expected-note{{Reference count incremented. The object now has a +1 retain count}}
  return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}}
}

void acquisitionViaCFFunction () {
  CFTypeRef leaked = CFGetSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object with a +0 retain count}}
  CFRetain(leaked); // expected-note{{Reference count incremented. The object now has a +1 retain count}}
  return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}}
}

void explicitDealloc () {
  id object = [[NSObject alloc] init]; // expected-note{{Method returns an Objective-C object with a +1 retain count}}
  [object dealloc]; // expected-note{{Object released by directly sending the '-dealloc' message}}
  [object class]; // expected-warning{{Reference-counted object is used after it is released}} // expected-note{{Reference-counted object is used after it is released}}
}

void implicitDealloc () {
  id object = [[NSObject alloc] init]; // expected-note{{Method returns an Objective-C object with a +1 retain count}}
  [object release]; // expected-note{{Object released}}
  [object class]; // expected-warning{{Reference-counted object is used after it is released}} // expected-note{{Reference-counted object is used after it is released}}
}

void overAutorelease () {
  id object = [[NSObject alloc] init]; // expected-note{{Method returns an Objective-C object with a +1 retain count}}
  [object autorelease]; // expected-note{{Object sent -autorelease message}}
  [object autorelease]; // expected-note{{Object sent -autorelease message}} 
  return; // expected-warning{{Object sent -autorelease too many times}} expected-note{{Object over-autoreleased: object was sent -autorelease 2 times but the object has a +1 retain count}} 
}

void autoreleaseUnowned (Foo *foo) {
  id object = foo.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}}
  [object autorelease]; // expected-note{{Object sent -autorelease message}} 
  return; // expected-warning{{Object sent -autorelease too many times}} expected-note{{Object over-autoreleased: object was sent -autorelease but the object has a +0 retain count}}
}

void makeCollectableIgnored () {
  CFTypeRef leaked = CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count}}
  CFMakeCollectable(leaked); // expected-note{{When GC is not enabled a call to 'CFMakeCollectable' has no effect on its argument}}
  NSMakeCollectable(leaked); // expected-note{{When GC is not enabled a call to 'NSMakeCollectable' has no effect on its argument}}
  return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}}
}

CFTypeRef CFCopyRuleViolation () {
  CFTypeRef object = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object with a +0 retain count}}
  return object; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
}

CFTypeRef CFGetRuleViolation () {
  CFTypeRef object = CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count}}
  return object; // expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'object' is returned from a function whose name ('CFGetRuleViolation') does not contain 'Copy' or 'Create'.  This violates the naming convention rules given in the Memory Management Guide for Core Foundation}}
}

@implementation Foo (FundamentalMemoryManagementRules)
- (id)copyViolation {
  id result = self.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}}
  return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
}

- (id)getViolation {
  id result = [[Foo alloc] init]; // expected-warning{{leak}} expected-note{{Method returns an Objective-C object with a +1 retain count}}
  return result; // expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'result' is returned from a method whose name ('getViolation') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'.  This violates the naming convention rules given in the Memory Management Guide for Cocoa}}
}

- (id)copyAutorelease {
  id result = [[Foo alloc] init]; // expected-note{{Method returns an Objective-C object with a +1 retain count}}
  [result autorelease]; // expected-note{{Object sent -autorelease message}}
  return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
}
@end