// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.RetainCount -verify %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.RetainCount -analyzer-inline-max-stack-depth=0 -verify %s #pragma clang arc_cf_code_audited begin typedef const void * CFTypeRef; extern CFTypeRef CFRetain(CFTypeRef cf); extern void CFRelease(CFTypeRef cf); #pragma clang arc_cf_code_audited end #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) #define CF_CONSUMED __attribute__((cf_consumed)) extern CFTypeRef CFCreate() CF_RETURNS_RETAINED; // A "safe" variant of CFRetain that doesn't crash when a null pointer is // retained. This is often defined by users in a similar manner. The // CF_RETURNS_RETAINED annotation is misleading here, because the function // is not supposed to return an object with a +1 retain count. Instead, it // is supposed to return an object with +(N+1) retain count, where N is // the original retain count of 'cf'. However, there is no good annotation // to use in this case, and it is pointless to provide such annotation // because the only use cases would be CFRetain and SafeCFRetain. // So instead we teach the analyzer to be able to accept such code // and ignore the misplaced annotation. CFTypeRef SafeCFRetain(CFTypeRef cf) CF_RETURNS_RETAINED { if (cf) { return CFRetain(cf); } return cf; } // A "safe" variant of CFRelease that doesn't crash when a null pointer is // released. The CF_CONSUMED annotation seems reasonable here. void SafeCFRelease(CFTypeRef CF_CONSUMED cf) { if (cf) CFRelease(cf); // no-warning (when inlined) } // The same thing, just with a different naming style. CFTypeRef retainCFType(CFTypeRef cf) CF_RETURNS_RETAINED { if (cf) { return CFRetain(cf); } return cf; } void releaseCFType(CFTypeRef CF_CONSUMED cf) { if (cf) CFRelease(cf); // no-warning (when inlined) } void escape(CFTypeRef cf); void makeSureTestsWork() { CFTypeRef cf = CFCreate(); CFRelease(cf); CFRelease(cf); // expected-warning{{Reference-counted object is used after it is released}} } // Make sure we understand that the second SafeCFRetain doesn't return an // object with +1 retain count, which we won't be able to release twice. void falseOverrelease(CFTypeRef cf) { SafeCFRetain(cf); SafeCFRetain(cf); SafeCFRelease(cf); SafeCFRelease(cf); // no-warning after inlining this. } // Regular CFRelease() should behave similarly. void sameWithNormalRelease(CFTypeRef cf) { SafeCFRetain(cf); SafeCFRetain(cf); CFRelease(cf); CFRelease(cf); // no-warning } // Make sure we understand that the second SafeCFRetain doesn't return an // object with +1 retain count, which would no longer be owned by us after // it escapes to escape() and released once. void falseReleaseNotOwned(CFTypeRef cf) { SafeCFRetain(cf); SafeCFRetain(cf); escape(cf); SafeCFRelease(cf); SafeCFRelease(cf); // no-warning after inlining this. } void testTheOtherNamingConvention(CFTypeRef cf) { retainCFType(cf); retainCFType(cf); releaseCFType(cf); releaseCFType(cf); // no-warning } |