2018-11-01 01:38:12 +08:00
// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount -analyzer-output=text -verify %s
2018-08-23 08:26:59 +08:00
2018-10-12 06:59:16 +08:00
struct OSMetaClass ;
2018-11-30 10:18:50 +08:00
# define OS_CONSUME __attribute__((os_consumed))
# define OS_RETURNS_RETAINED __attribute__((os_returns_retained))
# define OS_RETURNS_NOT_RETAINED __attribute__((os_returns_not_retained))
2018-10-24 07:11:50 +08:00
2018-10-12 06:59:16 +08:00
# define OSTypeID(type) (type::metaClass)
# define OSDynamicCast(type, inst) \
( ( type * ) OSMetaClassBase : : safeMetaCast ( ( inst ) , OSTypeID ( type ) ) )
2018-12-01 04:43:42 +08:00
using size_t = decltype ( sizeof ( int ) ) ;
2018-08-23 08:26:59 +08:00
struct OSObject {
virtual void retain ( ) ;
2018-10-24 07:11:30 +08:00
virtual void release ( ) { } ;
2018-12-01 04:43:42 +08:00
virtual void free ( ) ;
2018-08-23 08:26:59 +08:00
virtual ~ OSObject ( ) { }
2018-10-12 06:59:16 +08:00
2018-10-26 07:38:07 +08:00
unsigned int foo ( ) { return 42 ; }
2018-12-06 02:34:54 +08:00
virtual OS_RETURNS_NOT_RETAINED OSObject * identity ( ) ;
2018-10-12 06:59:16 +08:00
static OSObject * generateObject ( int ) ;
2018-11-30 10:18:10 +08:00
static OSObject * getObject ( ) ;
static OSObject * GetObject ( ) ;
2018-12-01 04:43:42 +08:00
static void * operator new ( size_t size ) ;
2018-10-12 06:59:16 +08:00
static const OSMetaClass * const metaClass ;
2018-08-23 08:26:59 +08:00
} ;
2018-11-01 01:38:46 +08:00
struct OSIterator : public OSObject {
2018-11-30 10:17:31 +08:00
static const OSMetaClass * const metaClass ;
2018-11-01 01:38:46 +08:00
} ;
2018-08-23 08:26:59 +08:00
struct OSArray : public OSObject {
unsigned int getCount ( ) ;
2018-12-06 02:34:54 +08:00
OSIterator * getIterator ( ) ;
OSObject * identity ( ) override ;
2018-12-07 06:06:44 +08:00
virtual OSObject * generateObject ( OSObject * input ) ;
2018-12-06 02:34:54 +08:00
virtual void consumeReference ( OS_CONSUME OSArray * other ) ;
2018-11-30 10:18:23 +08:00
static OSArray * generateArrayHasCode ( ) {
return new OSArray ;
}
2018-08-23 08:26:59 +08:00
static OSArray * withCapacity ( unsigned int capacity ) ;
2018-10-24 07:11:50 +08:00
static void consumeArray ( OS_CONSUME OSArray * array ) ;
static OSArray * consumeArrayHasCode ( OS_CONSUME OSArray * array ) {
return nullptr ;
}
static OS_RETURNS_NOT_RETAINED OSArray * MaskedGetter ( ) ;
static OS_RETURNS_RETAINED OSArray * getOoopsActuallyCreate ( ) ;
2018-10-12 06:59:16 +08:00
static const OSMetaClass * const metaClass ;
} ;
2018-12-06 02:34:54 +08:00
struct MyArray : public OSArray {
void consumeReference ( OSArray * other ) override ;
OSObject * identity ( ) override ;
2018-12-07 06:06:44 +08:00
OSObject * generateObject ( OSObject * input ) override ;
2018-12-06 02:34:54 +08:00
} ;
2018-10-24 07:11:50 +08:00
struct OtherStruct {
static void doNothingToArray ( OSArray * array ) ;
2018-10-26 07:38:41 +08:00
OtherStruct ( OSArray * arr ) ;
2018-10-24 07:11:50 +08:00
} ;
2018-10-12 06:59:16 +08:00
struct OSMetaClassBase {
static OSObject * safeMetaCast ( const OSObject * inst , const OSMetaClass * meta ) ;
2018-08-23 08:26:59 +08:00
} ;
2018-12-07 06:06:44 +08:00
void test_no_infinite_check_recursion ( MyArray * arr ) {
OSObject * input = new OSObject ;
OSObject * o = arr - > generateObject ( input ) ;
o - > release ( ) ;
input - > release ( ) ;
}
2018-12-06 02:34:54 +08:00
void check_param_attribute_propagation ( MyArray * parent ) {
OSArray * arr = new OSArray ;
parent - > consumeReference ( arr ) ;
}
unsigned int check_attribute_propagation ( OSArray * arr ) {
OSObject * other = arr - > identity ( ) ;
OSArray * casted = OSDynamicCast ( OSArray , other ) ;
if ( casted )
return casted - > getCount ( ) ;
return 0 ;
}
unsigned int check_attribute_indirect_propagation ( MyArray * arr ) {
OSObject * other = arr - > identity ( ) ;
OSArray * casted = OSDynamicCast ( OSArray , other ) ;
if ( casted )
return casted - > getCount ( ) ;
return 0 ;
}
2018-12-01 04:43:42 +08:00
void check_free_no_error ( ) {
OSArray * arr = OSArray : : withCapacity ( 10 ) ;
arr - > retain ( ) ;
arr - > retain ( ) ;
arr - > retain ( ) ;
arr - > free ( ) ;
}
void check_free_use_after_free ( ) {
OSArray * arr = OSArray : : withCapacity ( 10 ) ; // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
arr - > retain ( ) ; // expected-note{{Reference count incremented. The object now has a +2 retain count}}
arr - > free ( ) ; // expected-note{{Object released}}
arr - > retain ( ) ; // expected-warning{{Reference-counted object is used after it is released}}
// expected-note@-1{{Reference-counted object is used after it is released}}
}
unsigned int check_leak_explicit_new ( ) {
OSArray * arr = new OSArray ; // expected-note{{Operator new returns an OSObject of type OSArray with a +1 retain count}}
return arr - > getCount ( ) ; // expected-note{{Object leaked: allocated object of type OSArray is not referenced later in this execution path and has a retain count of +1}}
// expected-warning@-1{{Potential leak of an object of type OSArray}}
}
unsigned int check_leak_factory ( ) {
OSArray * arr = OSArray : : withCapacity ( 10 ) ; // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
return arr - > getCount ( ) ; // expected-note{{Object leaked: object allocated and stored into 'arr' is not referenced later in this execution path and has a retain count of +1}}
// expected-warning@-1{{Potential leak of an object stored into 'arr'}}
}
2018-11-30 10:18:10 +08:00
void check_get_object ( ) {
OSObject : : getObject ( ) ;
}
void check_Get_object ( ) {
OSObject : : GetObject ( ) ;
}
2018-11-01 01:38:46 +08:00
void check_custom_iterator_rule ( OSArray * arr ) {
OSIterator * it = arr - > getIterator ( ) ;
it - > release ( ) ;
}
2018-11-30 10:17:57 +08:00
void check_iterator_leak ( OSArray * arr ) {
2018-11-30 10:18:23 +08:00
arr - > getIterator ( ) ; // expected-note{{Call to method 'OSArray::getIterator' returns an OSObject of type OSIterator with a +1 retain count}}
} // expected-note{{Object leaked: allocated object of type OSIterator is not referenced later}}
// expected-warning@-1{{Potential leak of an object of type OSIterator}}
2018-11-30 10:17:57 +08:00
2018-10-24 07:11:50 +08:00
void check_no_invalidation ( ) {
2018-11-30 10:17:57 +08:00
OSArray * arr = OSArray : : withCapacity ( 10 ) ; // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
2018-10-24 07:11:50 +08:00
OtherStruct : : doNothingToArray ( arr ) ;
} // expected-warning{{Potential leak of an object stored into 'arr'}}
// expected-note@-1{{Object leaked}}
2018-10-26 07:38:41 +08:00
void check_no_invalidation_other_struct ( ) {
2018-11-30 10:17:57 +08:00
OSArray * arr = OSArray : : withCapacity ( 10 ) ; // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
2018-10-26 07:38:41 +08:00
OtherStruct other ( arr ) ; // expected-warning{{Potential leak}}
// expected-note@-1{{Object leaked}}
}
2018-11-01 01:38:29 +08:00
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 ( ) ;
} ;
2018-11-30 10:17:31 +08:00
OSArray * generateArray ( ) {
2018-11-30 10:18:23 +08:00
return OSArray : : withCapacity ( 10 ) ; // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
// expected-note@-1{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
2018-11-30 10:17:31 +08:00
}
unsigned int check_leak_good_error_message ( ) {
unsigned int out ;
{
OSArray * leaked = generateArray ( ) ; // expected-note{{Calling 'generateArray'}}
// expected-note@-1{{Returning from 'generateArray'}}
out = leaked - > getCount ( ) ; // expected-warning{{Potential leak of an object stored into 'leaked'}}
// expected-note@-1{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}}
}
return out ;
}
unsigned int check_leak_msg_temporary ( ) {
2018-11-30 10:17:44 +08:00
return generateArray ( ) - > getCount ( ) ; // expected-warning{{Potential leak of an object}}
// expected-note@-1{{Calling 'generateArray'}}
// expected-note@-2{{Returning from 'generateArray'}}
// expected-note@-3{{Object leaked: allocated object of type OSArray is not referenced later in this execution path and has a retain count of +1}}
2018-11-30 10:17:31 +08:00
}
2018-11-01 01:38:29 +08:00
void check_confusing_getters ( ) {
OSArray * arr = OSArray : : withCapacity ( 10 ) ;
ArrayOwner * AO = ArrayOwner : : create ( arr ) ;
AO - > getArray ( ) ;
AO - > release ( ) ;
arr - > release ( ) ;
}
2018-10-24 07:11:50 +08:00
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 ( ) ;
}
2018-10-12 06:59:16 +08:00
void check_dynamic_cast ( ) {
OSArray * arr = OSDynamicCast ( OSArray , OSObject : : generateObject ( 1 ) ) ;
arr - > release ( ) ;
}
2018-10-26 07:38:07 +08:00
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 ) {
2018-11-30 10:17:57 +08:00
OSArray * arr1 = OSArray : : withCapacity ( 10 ) ; // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject}}
2018-10-26 07:38:07 +08:00
OSArray * arr = OSDynamicCast ( OSArray , obj ) ;
if ( ! arr ) // expected-note{{Taking true branch}}
2018-11-30 10:17:31 +08:00
return ; // expected-warning{{Potential leak of an object stored into 'arr1'}}
2018-10-26 07:38:07 +08:00
// expected-note@-1{{Object leaked}}
arr1 - > release ( ) ;
}
2018-10-12 06:59:16 +08:00
void check_dynamic_cast_null_check ( ) {
2018-11-30 10:17:57 +08:00
OSArray * arr = OSDynamicCast ( OSArray , OSObject : : generateObject ( 1 ) ) ; // expected-note{{Call to method 'OSObject::generateObject' returns an OSObject}}
2018-10-26 07:38:07 +08:00
// expected-warning@-1{{Potential leak of an object}}
// expected-note@-2{{Object leaked}}
2018-10-12 06:59:16 +08:00
if ( ! arr )
return ;
arr - > release ( ) ;
}
2018-08-23 08:26:59 +08:00
void use_after_release ( ) {
2018-11-30 10:18:23 +08:00
OSArray * arr = OSArray : : withCapacity ( 10 ) ; // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
2018-08-23 08:26:59 +08:00
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 ( ) {
2018-11-30 10:18:23 +08:00
OSArray * arr = OSArray : : withCapacity ( 10 ) ; // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
2018-08-23 08:26:59 +08:00
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 ) {
2018-11-01 01:38:29 +08:00
// FIXME: summaries are not applied in case the source of the getter/setter
// is known.
// rdar://45681203
OSArray * arr = owner - > getArray ( ) ;
arr - > release ( ) ;
2018-08-23 08:26:59 +08:00
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 ) {
2018-11-30 10:17:57 +08:00
OSArray * arr = owner - > getArraySourceUnknown ( ) ; // expected-note{{Call to method 'ArrayOwner::getArraySourceUnknown' returns an OSObject of type OSArray with a +0 retain count}}
2018-08-23 08:26:59 +08:00
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 ( ) ;
}