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 |
| 2 | |
| 3 | #pragma clang arc_cf_code_audited begin |
| 4 | typedef const void * CFTypeRef; |
| 5 | extern CFTypeRef CFRetain(CFTypeRef cf); |
| 6 | extern void CFRelease(CFTypeRef cf); |
| 7 | #pragma clang arc_cf_code_audited end |
| 8 | |
| 9 | #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) |
| 10 | #define CF_CONSUMED __attribute__((cf_consumed)) |
| 11 | |
| 12 | extern CFTypeRef CFCreate() CF_RETURNS_RETAINED; |
| 13 | |
| 14 | // A "safe" variant of CFRetain that doesn't crash when a null pointer is |
| 15 | // retained. This is often defined by users in a similar manner. The |
| 16 | // CF_RETURNS_RETAINED annotation is misleading here, because the function |
| 17 | // is not supposed to return an object with a +1 retain count. Instead, it |
| 18 | // is supposed to return an object with +(N+1) retain count, where N is |
| 19 | // the original retain count of 'cf'. However, there is no good annotation |
| 20 | // to use in this case, and it is pointless to provide such annotation |
| 21 | // because the only use cases would be CFRetain and SafeCFRetain. |
| 22 | // So instead we teach the analyzer to be able to accept such code |
| 23 | // and ignore the misplaced annotation. |
| 24 | CFTypeRef SafeCFRetain(CFTypeRef cf) CF_RETURNS_RETAINED { |
| 25 | if (cf) { |
| 26 | return CFRetain(cf); |
| 27 | } |
| 28 | return cf; |
| 29 | } |
| 30 | |
| 31 | // A "safe" variant of CFRelease that doesn't crash when a null pointer is |
| 32 | // released. The CF_CONSUMED annotation seems reasonable here. |
| 33 | void SafeCFRelease(CFTypeRef CF_CONSUMED cf) { |
| 34 | if (cf) |
| 35 | CFRelease(cf); // no-warning (when inlined) |
| 36 | } |
| 37 | |
| 38 | void escape(CFTypeRef cf); |
| 39 | |
| 40 | void makeSureTestsWork() { |
| 41 | CFTypeRef cf = CFCreate(); |
| 42 | CFRelease(cf); |
| 43 | CFRelease(cf); // expected-warning{{Reference-counted object is used after it is released}} |
| 44 | } |
| 45 | |
| 46 | // Make sure we understand that the second SafeCFRetain doesn't return an |
| 47 | // object with +1 retain count, which we won't be able to release twice. |
| 48 | void falseOverrelease(CFTypeRef cf) { |
| 49 | SafeCFRetain(cf); |
| 50 | SafeCFRetain(cf); |
| 51 | SafeCFRelease(cf); |
| 52 | SafeCFRelease(cf); // no-warning after inlining this. |
| 53 | } |
| 54 | |
| 55 | // Regular CFRelease() should behave similarly. |
| 56 | void sameWithNormalRelease(CFTypeRef cf) { |
| 57 | SafeCFRetain(cf); |
| 58 | SafeCFRetain(cf); |
| 59 | CFRelease(cf); |
| 60 | CFRelease(cf); // no-warning |
| 61 | } |
| 62 | |
| 63 | // Make sure we understand that the second SafeCFRetain doesn't return an |
| 64 | // object with +1 retain count, which would no longer be owned by us after |
| 65 | // it escapes to escape() and released once. |
| 66 | void falseReleaseNotOwned(CFTypeRef cf) { |
| 67 | SafeCFRetain(cf); |
| 68 | SafeCFRetain(cf); |
| 69 | escape(cf); |
| 70 | SafeCFRelease(cf); |
| 71 | SafeCFRelease(cf); // no-warning after inlining this. |
| 72 | } |