Artem Dergachev | 2041cbd | 2017-10-13 19:10:42 +0000 | [diff] [blame] | 1 | // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.RetainCount -verify %s |
Artem Dergachev | f7281b4 | 2018-04-19 23:00:22 +0000 | [diff] [blame^] | 2 | // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.RetainCount -analyzer-inline-max-stack-depth=0 -verify %s |
Artem Dergachev | 2041cbd | 2017-10-13 19:10:42 +0000 | [diff] [blame] | 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 | |
Artem Dergachev | f7281b4 | 2018-04-19 23:00:22 +0000 | [diff] [blame^] | 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 | |
Artem Dergachev | 2041cbd | 2017-10-13 19:10:42 +0000 | [diff] [blame] | 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 | } |
Artem Dergachev | f7281b4 | 2018-04-19 23:00:22 +0000 | [diff] [blame^] | 87 | |
| 88 | void testTheOtherNamingConvention(CFTypeRef cf) { |
| 89 | retainCFType(cf); |
| 90 | retainCFType(cf); |
| 91 | releaseCFType(cf); |
| 92 | releaseCFType(cf); // no-warning |
| 93 | } |