159 lines
5.8 KiB
Plaintext
159 lines
5.8 KiB
Plaintext
|
// RUN: %clang_analyze_cc1 -fblocks -fobjc-arc -verify %s -Wno-objc-root-class \
|
||
|
// RUN: -analyzer-checker=core \
|
||
|
// RUN: -analyzer-checker=alpha.core.StackAddressAsyncEscape \
|
||
|
// RUN: -analyzer-checker=nullability \
|
||
|
// RUN: -analyzer-checker=osx
|
||
|
|
||
|
#include "Inputs/system-header-simulator-for-nullability.h"
|
||
|
#include "os_object_base.h"
|
||
|
|
||
|
struct OSIterator : public OSObject {
|
||
|
static const OSMetaClass * const metaClass;
|
||
|
};
|
||
|
|
||
|
@interface TestObject : NSObject
|
||
|
- (int *_Nonnull)returnsNonnull;
|
||
|
- (int *_Nullable)returnsNullable;
|
||
|
- (int *)returnsUnspecified;
|
||
|
- (void)takesNonnull:(int *_Nonnull)p;
|
||
|
- (void)takesNullable:(int *_Nullable)p;
|
||
|
- (void)takesUnspecified:(int *)p;
|
||
|
@property(readonly, strong) NSString *stuff;
|
||
|
@end
|
||
|
|
||
|
TestObject * getUnspecifiedTestObject();
|
||
|
TestObject *_Nonnull getNonnullTestObject();
|
||
|
TestObject *_Nullable getNullableTestObject();
|
||
|
|
||
|
int getRandom();
|
||
|
|
||
|
typedef struct Dummy { int val; } Dummy;
|
||
|
|
||
|
void takesNullable(Dummy *_Nullable);
|
||
|
void takesNonnull(Dummy *_Nonnull);
|
||
|
void takesUnspecified(Dummy *);
|
||
|
|
||
|
Dummy *_Nullable returnsNullable();
|
||
|
Dummy *_Nonnull returnsNonnull();
|
||
|
Dummy *returnsUnspecified();
|
||
|
int *_Nullable returnsNullableInt();
|
||
|
|
||
|
template <typename T> T *eraseNullab(T *p) { return p; }
|
||
|
|
||
|
void takesAttrNonnull(Dummy *p) __attribute((nonnull(1)));
|
||
|
|
||
|
void testBasicRules() {
|
||
|
// FIXME: None of these should be tied to a modeling checker.
|
||
|
Dummy *p = returnsNullable();
|
||
|
int *ptr = returnsNullableInt();
|
||
|
// Make every dereference a different path to avoid sinks after errors.
|
||
|
switch (getRandom()) {
|
||
|
case 0: {
|
||
|
Dummy &r = *p; // expected-warning {{Nullable pointer is dereferenced [nullability.NullableDereferenced]}}
|
||
|
} break;
|
||
|
case 1: {
|
||
|
int b = p->val; // expected-warning {{Nullable pointer is dereferenced [nullability.NullableDereferenced]}}
|
||
|
} break;
|
||
|
case 2: {
|
||
|
int stuff = *ptr; // expected-warning {{Nullable pointer is dereferenced [nullability.NullableDereferenced]}}
|
||
|
} break;
|
||
|
case 3:
|
||
|
takesNonnull(p); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter [nullability.NullablePassedToNonnull]}}
|
||
|
break;
|
||
|
case 4: {
|
||
|
Dummy d;
|
||
|
takesNullable(&d);
|
||
|
Dummy dd(d);
|
||
|
break;
|
||
|
}
|
||
|
case 5:
|
||
|
takesAttrNonnull(p); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null [nullability.NullableDereferenced]}}
|
||
|
break;
|
||
|
default: { Dummy d = *p; } break; // expected-warning {{Nullable pointer is dereferenced [nullability.NullableDereferenced]}}
|
||
|
}
|
||
|
if (p) {
|
||
|
takesNonnull(p);
|
||
|
if (getRandom()) {
|
||
|
Dummy &r = *p;
|
||
|
} else {
|
||
|
int b = p->val;
|
||
|
}
|
||
|
}
|
||
|
Dummy *q = 0;
|
||
|
if (getRandom()) {
|
||
|
takesNullable(q);
|
||
|
// FIXME: This shouldn't be tied to a modeling checker.
|
||
|
takesNonnull(q); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter [nullability.NullPassedToNonnull]}}
|
||
|
}
|
||
|
Dummy a;
|
||
|
Dummy *_Nonnull nonnull = &a;
|
||
|
// FIXME: This shouldn't be tied to a modeling checker.
|
||
|
nonnull = q; // expected-warning {{Null assigned to a pointer which is expected to have non-null value [nullability.NullPassedToNonnull]}}
|
||
|
q = &a;
|
||
|
takesNullable(q);
|
||
|
takesNonnull(q);
|
||
|
}
|
||
|
|
||
|
typedef int NSInteger;
|
||
|
typedef struct _NSZone NSZone;
|
||
|
@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator;
|
||
|
@class NSDictionary;
|
||
|
@interface NSError : NSObject <NSCopying, NSCoding> {}
|
||
|
+ (id)errorWithDomain:(NSString *)domain code:(NSInteger)code userInfo:(NSDictionary *)dict;
|
||
|
@end
|
||
|
|
||
|
struct __CFError {};
|
||
|
typedef struct __CFError* CFErrorRef;
|
||
|
|
||
|
void foo(CFErrorRef* error) { // expected-warning{{Function accepting CFErrorRef* should have a non-void return value to indicate whether or not an error occurred [osx.coreFoundation.CFError]}}
|
||
|
// FIXME: This shouldn't be tied to a modeling checker.
|
||
|
*error = 0; // expected-warning {{Potential null dereference. According to coding standards documented in CoreFoundation/CFError.h the parameter may be null [osx.coreFoundation.CFError]}}
|
||
|
}
|
||
|
|
||
|
@interface A
|
||
|
- (void)myMethodWhichMayFail:(NSError **)error;
|
||
|
@end
|
||
|
|
||
|
@implementation A
|
||
|
- (void)myMethodWhichMayFail:(NSError **)error { // expected-warning {{Method accepting NSError** should have a non-void return value to indicate whether or not an error occurred [osx.cocoa.NSError]}}
|
||
|
*error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // expected-warning {{Potential null dereference. According to coding standards in 'Creating and Returning NSError Objects' the parameter may be null [osx.cocoa.NSError]}}
|
||
|
}
|
||
|
@end
|
||
|
|
||
|
bool write_into_out_param_on_success(OS_RETURNS_RETAINED OSObject **obj);
|
||
|
|
||
|
void use_out_param_leak() {
|
||
|
OSObject *obj;
|
||
|
// FIXME: This shouldn't be tied to a modeling checker.
|
||
|
write_into_out_param_on_success(&obj); // expected-warning{{Potential leak of an object stored into 'obj' [osx.cocoa.RetainCount]}}
|
||
|
}
|
||
|
|
||
|
typedef struct dispatch_queue_s *dispatch_queue_t;
|
||
|
typedef void (^dispatch_block_t)(void);
|
||
|
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
|
||
|
typedef long dispatch_once_t;
|
||
|
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
|
||
|
typedef long dispatch_time_t;
|
||
|
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
|
||
|
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
|
||
|
|
||
|
extern dispatch_queue_t queue;
|
||
|
extern dispatch_once_t *predicate;
|
||
|
extern dispatch_time_t when;
|
||
|
|
||
|
dispatch_block_t get_leaking_block() {
|
||
|
int leaked_x = 791;
|
||
|
int *p = &leaked_x;
|
||
|
return ^void(void) {
|
||
|
*p = 1;
|
||
|
};
|
||
|
// expected-warning@-3 {{Address of stack memory associated with local variable 'leaked_x' \
|
||
|
is captured by a returned block [core.StackAddressEscape]}}
|
||
|
}
|
||
|
|
||
|
void test_returned_from_func_block_async() {
|
||
|
dispatch_async(queue, get_leaking_block());
|
||
|
// expected-warning@-1 {{Address of stack memory associated with local variable 'leaked_x' \
|
||
|
is captured by an asynchronously-executed block [alpha.core.StackAddressAsyncEscape]}}
|
||
|
}
|