1 // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.RetainCount -verify %s
2 // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.RetainCount -analyzer-inline-max-stack-depth=0 -verify %s
3 
4 #pragma clang arc_cf_code_audited begin
5 typedef const void * CFTypeRef;
6 extern CFTypeRef CFRetain(CFTypeRef cf);
7 extern void CFRelease(CFTypeRef cf);
8 #pragma clang arc_cf_code_audited end
9 
10 #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
11 #define CF_CONSUMED __attribute__((cf_consumed))
12 
13 extern CFTypeRef CFCreate() CF_RETURNS_RETAINED;
14 
15 // A "safe" variant of CFRetain that doesn't crash when a null pointer is
16 // retained. This is often defined by users in a similar manner. The
17 // CF_RETURNS_RETAINED annotation is misleading here, because the function
18 // is not supposed to return an object with a +1 retain count. Instead, it
19 // is supposed to return an object with +(N+1) retain count, where N is
20 // the original retain count of 'cf'. However, there is no good annotation
21 // to use in this case, and it is pointless to provide such annotation
22 // because the only use cases would be CFRetain and SafeCFRetain.
23 // So instead we teach the analyzer to be able to accept such code
24 // and ignore the misplaced annotation.
SafeCFRetain(CFTypeRef cf)25 CFTypeRef SafeCFRetain(CFTypeRef cf) CF_RETURNS_RETAINED {
26   if (cf) {
27     return CFRetain(cf);
28   }
29   return cf;
30 }
31 
32 // A "safe" variant of CFRelease that doesn't crash when a null pointer is
33 // released. The CF_CONSUMED annotation seems reasonable here.
SafeCFRelease(CFTypeRef CF_CONSUMED cf)34 void SafeCFRelease(CFTypeRef CF_CONSUMED cf) {
35   if (cf)
36     CFRelease(cf); // no-warning (when inlined)
37 }
38 
39 // The same thing, just with a different naming style.
retainCFType(CFTypeRef cf)40 CFTypeRef retainCFType(CFTypeRef cf) CF_RETURNS_RETAINED {
41   if (cf) {
42     return CFRetain(cf);
43   }
44   return cf;
45 }
46 
releaseCFType(CFTypeRef CF_CONSUMED cf)47 void releaseCFType(CFTypeRef CF_CONSUMED cf) {
48   if (cf)
49     CFRelease(cf); // no-warning (when inlined)
50 }
51 
52 void escape(CFTypeRef cf);
53 
makeSureTestsWork()54 void makeSureTestsWork() {
55   CFTypeRef cf = CFCreate();
56   CFRelease(cf);
57   CFRelease(cf); // expected-warning{{Reference-counted object is used after it is released}}
58 }
59 
60 // Make sure we understand that the second SafeCFRetain doesn't return an
61 // object with +1 retain count, which we won't be able to release twice.
falseOverrelease(CFTypeRef cf)62 void falseOverrelease(CFTypeRef cf) {
63   SafeCFRetain(cf);
64   SafeCFRetain(cf);
65   SafeCFRelease(cf);
66   SafeCFRelease(cf); // no-warning after inlining this.
67 }
68 
69 // Regular CFRelease() should behave similarly.
sameWithNormalRelease(CFTypeRef cf)70 void sameWithNormalRelease(CFTypeRef cf) {
71   SafeCFRetain(cf);
72   SafeCFRetain(cf);
73   CFRelease(cf);
74   CFRelease(cf); // no-warning
75 }
76 
77 // Make sure we understand that the second SafeCFRetain doesn't return an
78 // object with +1 retain count, which would no longer be owned by us after
79 // it escapes to escape() and released once.
falseReleaseNotOwned(CFTypeRef cf)80 void falseReleaseNotOwned(CFTypeRef cf) {
81   SafeCFRetain(cf);
82   SafeCFRetain(cf);
83   escape(cf);
84   SafeCFRelease(cf);
85   SafeCFRelease(cf); // no-warning after inlining this.
86 }
87 
testTheOtherNamingConvention(CFTypeRef cf)88 void testTheOtherNamingConvention(CFTypeRef cf) {
89   retainCFType(cf);
90   retainCFType(cf);
91   releaseCFType(cf);
92   releaseCFType(cf); // no-warning
93 }
94