llvm-project/clang/test/Analysis/osobject-retain-release.cpp

231 lines
7.1 KiB
C++

// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount -analyzer-output=text -verify %s
struct OSMetaClass;
#define OS_CONSUME __attribute__((annotate("rc_ownership_consumed")))
#define OS_RETURNS_RETAINED __attribute__((annotate("rc_ownership_returns_retained")))
#define OS_RETURNS_NOT_RETAINED __attribute__((annotate("rc_ownership_returns_not_retained")))
#define OSTypeID(type) (type::metaClass)
#define OSDynamicCast(type, inst) \
((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type)))
struct OSObject {
virtual void retain();
virtual void release() {};
virtual ~OSObject(){}
unsigned int foo() { return 42; }
static OSObject *generateObject(int);
static const OSMetaClass * const metaClass;
};
struct OSIterator : public OSObject {
};
struct OSArray : public OSObject {
unsigned int getCount();
static OSArray *withCapacity(unsigned int capacity);
static void consumeArray(OS_CONSUME OSArray * array);
static OSArray* consumeArrayHasCode(OS_CONSUME OSArray * array) {
return nullptr;
}
OSIterator * getIterator();
static OS_RETURNS_NOT_RETAINED OSArray *MaskedGetter();
static OS_RETURNS_RETAINED OSArray *getOoopsActuallyCreate();
static const OSMetaClass * const metaClass;
};
struct OtherStruct {
static void doNothingToArray(OSArray *array);
OtherStruct(OSArray *arr);
};
struct OSMetaClassBase {
static OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta);
};
void check_custom_iterator_rule(OSArray *arr) {
OSIterator *it = arr->getIterator();
it->release();
}
void check_no_invalidation() {
OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}}
OtherStruct::doNothingToArray(arr);
} // expected-warning{{Potential leak of an object stored into 'arr'}}
// expected-note@-1{{Object leaked}}
void check_no_invalidation_other_struct() {
OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}}
OtherStruct other(arr); // expected-warning{{Potential leak}}
// expected-note@-1{{Object leaked}}
}
struct ArrayOwner : public OSObject {
OSArray *arr;
ArrayOwner(OSArray *arr) : arr(arr) {}
static ArrayOwner* create(OSArray *arr) {
return new ArrayOwner(arr);
}
OSArray *getArray() {
return arr;
}
OSArray *createArray() {
return OSArray::withCapacity(10);
}
OSArray *createArraySourceUnknown();
OSArray *getArraySourceUnknown();
};
void check_confusing_getters() {
OSArray *arr = OSArray::withCapacity(10);
ArrayOwner *AO = ArrayOwner::create(arr);
AO->getArray();
AO->release();
arr->release();
}
void check_rc_consumed() {
OSArray *arr = OSArray::withCapacity(10);
OSArray::consumeArray(arr);
}
void check_rc_consume_temporary() {
OSArray::consumeArray(OSArray::withCapacity(10));
}
void check_rc_getter() {
OSArray *arr = OSArray::MaskedGetter();
(void)arr;
}
void check_rc_create() {
OSArray *arr = OSArray::getOoopsActuallyCreate();
arr->release();
}
void check_dynamic_cast() {
OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1));
arr->release();
}
unsigned int check_dynamic_cast_no_null_on_orig(OSObject *obj) {
OSArray *arr = OSDynamicCast(OSArray, obj);
if (arr) {
return arr->getCount();
} else {
// The fact that dynamic cast has failed should not imply that
// the input object was null.
return obj->foo(); // no-warning
}
}
void check_dynamic_cast_null_branch(OSObject *obj) {
OSArray *arr1 = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject}}
OSArray *arr = OSDynamicCast(OSArray, obj);
if (!arr) // expected-note{{Taking true branch}}
return; // expected-warning{{Potential leak}}
// expected-note@-1{{Object leaked}}
arr1->release();
}
void check_dynamic_cast_null_check() {
OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); // expected-note{{Call to function 'generateObject' returns an OSObject}}
// expected-warning@-1{{Potential leak of an object}}
// expected-note@-2{{Object leaked}}
if (!arr)
return;
arr->release();
}
void use_after_release() {
OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}}
arr->release(); // expected-note{{Object released}}
arr->getCount(); // expected-warning{{Reference-counted object is used after it is released}}
// expected-note@-1{{Reference-counted object is used after it is released}}
}
void potential_leak() {
OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}}
arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}}
arr->getCount();
} // expected-warning{{Potential leak of an object stored into 'arr'}}
// expected-note@-1{{Object leaked: object allocated and stored into 'arr' is not referenced later in this execution path and has a retain count of +1}}
void proper_cleanup() {
OSArray *arr = OSArray::withCapacity(10); // +1
arr->retain(); // +2
arr->release(); // +1
arr->getCount();
arr->release(); // 0
}
unsigned int no_warning_on_getter(ArrayOwner *owner) {
OSArray *arr = owner->getArray();
return arr->getCount();
}
unsigned int warn_on_overrelease(ArrayOwner *owner) {
// FIXME: summaries are not applied in case the source of the getter/setter
// is known.
// rdar://45681203
OSArray *arr = owner->getArray();
arr->release();
return arr->getCount();
}
unsigned int nowarn_on_release_of_created(ArrayOwner *owner) {
OSArray *arr = owner->createArray();
unsigned int out = arr->getCount();
arr->release();
return out;
}
unsigned int nowarn_on_release_of_created_source_unknown(ArrayOwner *owner) {
OSArray *arr = owner->createArraySourceUnknown();
unsigned int out = arr->getCount();
arr->release();
return out;
}
unsigned int no_warn_ok_release(ArrayOwner *owner) {
OSArray *arr = owner->getArray(); // +0
arr->retain(); // +1
arr->release(); // +0
return arr->getCount(); // no-warning
}
unsigned int warn_on_overrelease_with_unknown_source(ArrayOwner *owner) {
OSArray *arr = owner->getArraySourceUnknown(); // expected-note{{function call returns an OSObject of type struct OSArray * with a +0 retain count}}
arr->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
// expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
return arr->getCount();
}
unsigned int ok_release_with_unknown_source(ArrayOwner *owner) {
OSArray *arr = owner->getArraySourceUnknown(); // +0
arr->retain(); // +1
arr->release(); // +0
return arr->getCount();
}