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. |
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. |
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. |
40 | CFTypeRef retainCFType(CFTypeRef cf) CF_RETURNS_RETAINED { |
41 | if (cf) { |
42 | return CFRetain(cf); |
43 | } |
44 | return cf; |
45 | } |
46 | |
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 | |
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. |
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. |
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. |
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 | |
88 | void testTheOtherNamingConvention(CFTypeRef cf) { |
89 | retainCFType(cf); |
90 | retainCFType(cf); |
91 | releaseCFType(cf); |
92 | releaseCFType(cf); // no-warning |
93 | } |
94 | |