|
| 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 | +} |
0 commit comments