diff options
Diffstat (limited to 'test/Analysis')
-rw-r--r-- | test/Analysis/bstring.c | 36 | ||||
-rw-r--r-- | test/Analysis/misc-ps-eager-assume.m | 1 | ||||
-rw-r--r-- | test/Analysis/nullptr.cpp | 8 | ||||
-rw-r--r-- | test/Analysis/objc-arc.m | 149 | ||||
-rw-r--r-- | test/Analysis/pr4209.m | 6 | ||||
-rw-r--r-- | test/Analysis/retain-release-gc-only.m | 1 | ||||
-rw-r--r-- | test/Analysis/retain-release-path-notes-gc.m | 74 | ||||
-rw-r--r-- | test/Analysis/retain-release-path-notes.m | 123 | ||||
-rw-r--r-- | test/Analysis/retain-release.m | 87 | ||||
-rw-r--r-- | test/Analysis/retain-release.mm | 1 | ||||
-rw-r--r-- | test/Analysis/string-fail.c | 113 | ||||
-rw-r--r-- | test/Analysis/string.c | 402 | ||||
-rw-r--r-- | test/Analysis/uninit-ps-rdar6145427.m | 5 | ||||
-rw-r--r-- | test/Analysis/uninit-vals-ps-region.m | 9 | ||||
-rw-r--r-- | test/Analysis/uninit-vals.m | 9 | ||||
-rw-r--r-- | test/Analysis/variadic-method-types.m | 8 |
16 files changed, 895 insertions, 137 deletions
diff --git a/test/Analysis/bstring.c b/test/Analysis/bstring.c index 68bbb1a5b2f5a..cd43e36d03510 100644 --- a/test/Analysis/bstring.c +++ b/test/Analysis/bstring.c @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,cplusplus.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,cplusplus.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,cplusplus.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,unix.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,unix.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,unix.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s //===----------------------------------------------------------------------=== // Declarations @@ -64,14 +64,14 @@ void memcpy1 () { char src[] = {1, 2, 3, 4}; char dst[10]; - memcpy(dst, src, 5); // expected-warning{{Byte string function accesses out-of-bound array element}} + memcpy(dst, src, 5); // expected-warning{{Memory copy function accesses out-of-bound array element}} } void memcpy2 () { char src[] = {1, 2, 3, 4}; char dst[1]; - memcpy(dst, src, 4); // expected-warning{{Byte string function overflows destination buffer}} + memcpy(dst, src, 4); // expected-warning{{Memory copy function overflows destination buffer}} } void memcpy3 () { @@ -85,14 +85,14 @@ void memcpy4 () { char src[] = {1, 2, 3, 4}; char dst[10]; - memcpy(dst+2, src+2, 3); // expected-warning{{Byte string function accesses out-of-bound array element}} + memcpy(dst+2, src+2, 3); // expected-warning{{Memory copy function accesses out-of-bound array element}} } void memcpy5() { char src[] = {1, 2, 3, 4}; char dst[3]; - memcpy(dst+2, src+2, 2); // expected-warning{{Byte string function overflows destination buffer}} + memcpy(dst+2, src+2, 2); // expected-warning{{Memory copy function overflows destination buffer}} } void memcpy6() { @@ -118,12 +118,12 @@ void memcpy9() { void memcpy10() { char a[4] = {0}; - memcpy(0, a, 4); // expected-warning{{Null pointer argument in call to byte string function}} + memcpy(0, a, 4); // expected-warning{{Null pointer argument in call to memory copy function}} } void memcpy11() { char a[4] = {0}; - memcpy(a, 0, 4); // expected-warning{{Null pointer argument in call to byte string function}} + memcpy(a, 0, 4); // expected-warning{{Null pointer argument in call to memory copy function}} } void memcpy12() { @@ -144,7 +144,7 @@ void memcpy_unknown_size (size_t n) { void memcpy_unknown_size_warn (size_t n) { char a[4]; - if (memcpy(a, 0, n) != a) // expected-warning{{Null pointer argument in call to byte string function}} + if (memcpy(a, 0, n) != a) // expected-warning{{Null pointer argument in call to memory copy function}} (void)*(char*)0; // no-warning } @@ -186,14 +186,14 @@ void mempcpy1 () { char src[] = {1, 2, 3, 4}; char dst[10]; - mempcpy(dst, src, 5); // expected-warning{{Byte string function accesses out-of-bound array element}} + mempcpy(dst, src, 5); // expected-warning{{Memory copy function accesses out-of-bound array element}} } void mempcpy2 () { char src[] = {1, 2, 3, 4}; char dst[1]; - mempcpy(dst, src, 4); // expected-warning{{Byte string function overflows destination buffer}} + mempcpy(dst, src, 4); // expected-warning{{Memory copy function overflows destination buffer}} } void mempcpy3 () { @@ -207,14 +207,14 @@ void mempcpy4 () { char src[] = {1, 2, 3, 4}; char dst[10]; - mempcpy(dst+2, src+2, 3); // expected-warning{{Byte string function accesses out-of-bound array element}} + mempcpy(dst+2, src+2, 3); // expected-warning{{Memory copy function accesses out-of-bound array element}} } void mempcpy5() { char src[] = {1, 2, 3, 4}; char dst[3]; - mempcpy(dst+2, src+2, 2); // expected-warning{{Byte string function overflows destination buffer}} + mempcpy(dst+2, src+2, 2); // expected-warning{{Memory copy function overflows destination buffer}} } void mempcpy6() { @@ -240,12 +240,12 @@ void mempcpy9() { void mempcpy10() { char a[4] = {0}; - mempcpy(0, a, 4); // expected-warning{{Null pointer argument in call to byte string function}} + mempcpy(0, a, 4); // expected-warning{{Null pointer argument in call to memory copy function}} } void mempcpy11() { char a[4] = {0}; - mempcpy(a, 0, 4); // expected-warning{{Null pointer argument in call to byte string function}} + mempcpy(a, 0, 4); // expected-warning{{Null pointer argument in call to memory copy function}} } void mempcpy12() { @@ -260,7 +260,7 @@ void mempcpy13() { void mempcpy_unknown_size_warn (size_t n) { char a[4]; - if (mempcpy(a, 0, n) != a) // expected-warning{{Null pointer argument in call to byte string function}} + if (mempcpy(a, 0, n) != a) // expected-warning{{Null pointer argument in call to memory copy function}} (void)*(char*)0; // no-warning } diff --git a/test/Analysis/misc-ps-eager-assume.m b/test/Analysis/misc-ps-eager-assume.m index 649c4b07f5439..4380a187b8002 100644 --- a/test/Analysis/misc-ps-eager-assume.m +++ b/test/Analysis/misc-ps-eager-assume.m @@ -12,6 +12,7 @@ typedef struct _NSZone NSZone; @end @protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end @interface NSObject <NSObject> {} + (id)alloc; +- (id)init; @end typedef struct {} NSFastEnumerationState; @protocol NSFastEnumeration - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len; diff --git a/test/Analysis/nullptr.cpp b/test/Analysis/nullptr.cpp index b74a5abcdfa4b..6f78baebfe22a 100644 --- a/test/Analysis/nullptr.cpp +++ b/test/Analysis/nullptr.cpp @@ -39,3 +39,11 @@ void foo4(void) { *np = 0; // no-warning } + +int pr10372(void *& x) { + // GNU null is a pointer-sized integer, not a pointer. + x = __null; + // This used to crash. + return __null; +} + diff --git a/test/Analysis/objc-arc.m b/test/Analysis/objc-arc.m new file mode 100644 index 0000000000000..6b22fd099b06e --- /dev/null +++ b/test/Analysis/objc-arc.m @@ -0,0 +1,149 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core -analyzer-checker=deadcode -analyzer-store=region -verify -fblocks -analyzer-opt-analyze-nested-blocks -fobjc-nonfragile-abi -fobjc-arc %s + +typedef signed char BOOL; +typedef struct _NSZone NSZone; +@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator; + +@protocol NSObject +- (BOOL)isEqual:(id)object; +@end +@protocol NSCopying +- (id)copyWithZone:(NSZone *)zone; +@end +@protocol NSCoding +- (void)encodeWithCoder:(NSCoder *)aCoder; +@end +@interface NSObject <NSObject> {} ++ (id)alloc; +@end +typedef const struct __CFAllocator * CFAllocatorRef; +extern const CFAllocatorRef kCFAllocatorDefault; +typedef double CFTimeInterval; +typedef CFTimeInterval CFAbsoluteTime; +extern CFAbsoluteTime CFAbsoluteTimeGetCurrent(void); +typedef const struct __CFDate * CFDateRef; +extern CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at); + +typedef const void* objc_objectptr_t; +__attribute__((ns_returns_retained)) id objc_retainedObject(objc_objectptr_t __attribute__((cf_consumed)) pointer); +__attribute__((ns_returns_not_retained)) id objc_unretainedObject(objc_objectptr_t pointer); + +// Test the analyzer is working at all. +void test_working() { + int *p = 0; + *p = 0xDEADBEEF; // expected-warning {{null}} +} + +// Test that in ARC mode that blocks are correctly automatically copied +// and not flagged as warnings by the analyzer. +typedef void (^Block)(void); +void testblock_bar(int x); + +Block testblock_foo(int x) { + Block b = ^{ testblock_bar(x); }; + return b; // no-warning +} + +Block testblock_baz(int x) { + return ^{ testblock_bar(x); }; // no-warning +} + +Block global_block; + +void testblock_qux(int x) { + global_block = ^{ testblock_bar(x); }; // no-warning +} + +// Test that Objective-C pointers are null initialized. +void test_nil_initialized() { + id x; + if (x == 0) + return; + int *p = 0; + *p = 0xDEADBEEF; // no-warning +} + +// Test that we don't flag leaks of Objective-C objects. +void test_alloc() { + [NSObject alloc]; // no-warning +} + +// Test that CF allocations are still caught as leaks. +void test_cf_leak() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); // expected-warning {{Potential leak}} + (void) date; +} + +// Test that 'init' methods do not try to claim ownerhip of an *unowned* allocated object +// in ARC mode. +@interface RDar9424890_A : NSObject +- (id)initWithCleaner:(int)pop mop:(NSString *)mop ; +- (RDar9424890_A *)rdar9424890:(NSString *)identifier; +@end +@interface RDar9424890_B : NSObject +@end +@implementation RDar9424890_B +- (RDar9424890_A *)obj:(RDar9424890_A *)obj { + static NSString *WhizFiz = @"WhizFiz"; + RDar9424890_A *cell = [obj rdar9424890:WhizFiz]; + if (cell == ((void*)0)) { + cell = [[RDar9424890_A alloc] initWithCleaner:0 mop:WhizFiz]; // no-warning + } + return cell; +} +@end + +// Test that dead store checking works in the prescence of "cleanups" in the AST. +void rdar9424882() { + id x = [NSObject alloc]; // expected-warning {{Value stored to 'x' during its initialization is never read}} +} + +// Test +typedef const void *CFTypeRef; +typedef const struct __CFString *CFStringRef; + +@interface NSString +- (id) self; +@end + +CFTypeRef CFCreateSomething(); +CFStringRef CFCreateString(); +CFTypeRef CFGetSomething(); +CFStringRef CFGetString(); + +id CreateSomething(); +NSString *CreateNSString(); + +void from_cf() { + id obj1 = (__bridge_transfer id)CFCreateSomething(); // expected-warning{{never read}} + id obj2 = (__bridge_transfer NSString*)CFCreateString(); + [obj2 self]; // Add a use, to show we can use the object after it has been transfered. + id obj3 = (__bridge id)CFGetSomething(); + [obj3 self]; // Add a use, to show we can use the object after it has been bridged. + id obj4 = (__bridge NSString*)CFGetString(); // expected-warning{{never read}} + id obj5 = (__bridge id)CFCreateSomething(); // expected-warning{{never read}} expected-warning{{leak}} + id obj6 = (__bridge NSString*)CFCreateString(); // expected-warning{{never read}} expected-warning{{leak}} +} + +void to_cf(id obj) { + CFTypeRef cf1 = (__bridge_retained CFTypeRef)CreateSomething(); // expected-warning{{never read}} + CFStringRef cf2 = (__bridge_retained CFStringRef)CreateNSString(); // expected-warning{{never read}} + CFTypeRef cf3 = (__bridge CFTypeRef)CreateSomething(); // expected-warning{{never read}} + CFStringRef cf4 = (__bridge CFStringRef)CreateNSString(); // expected-warning{{never read}} +} + +void test_objc_retainedObject() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); + id x = objc_retainedObject(date); + (void) x; +} + +void test_objc_unretainedObject() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); // expected-warning {{Potential leak}} + id x = objc_unretainedObject(date); + (void) x; +} + diff --git a/test/Analysis/pr4209.m b/test/Analysis/pr4209.m index 1e0fd32fcd03b..e3bc5cc143331 100644 --- a/test/Analysis/pr4209.m +++ b/test/Analysis/pr4209.m @@ -49,17 +49,17 @@ CMProfileLocation; GSEbayCategory *rootCategory; } - (NSMutableDictionary*)categoryDictionaryForCategoryID:(int)inID inRootTreeCategories:(NSMutableArray*)inRootTreeCategories; // expected-note {{method definition for 'categoryDictionaryForCategoryID:inRootTreeCategories:' not found}} --(NSString*) categoryID; // expected-note {{method definition for 'categoryID' not found}} +-(NSString*) categoryID; // expected-note {{method definition for 'categoryID' not found}} expected-note {{using}} @end @interface GSEbayCategory : NSObject <NSCoding> { } -- (int) categoryID; +- (int) categoryID; // expected-note {{also found}} - (GSEbayCategory *) parent; - (GSEbayCategory*) subcategoryWithID:(int) inID; @end @implementation GBCategoryChooserPanelController + (int) chooseCategoryIDFromCategories:(NSArray*) inCategories searchRequest:(GBSearchRequest*)inRequest parentWindow:(NSWindow*) inParent { // expected-warning {{incomplete implementation}} return 0; } - (void) addCategory:(EBayCategoryType*)inCategory toRootTreeCategory:(NSMutableArray*)inRootTreeCategories { - GSEbayCategory *category = [rootCategory subcategoryWithID:[[inCategory categoryID] intValue]]; + GSEbayCategory *category = [rootCategory subcategoryWithID:[[inCategory categoryID] intValue]]; // expected-warning {{multiple methods named 'categoryID' found}} if (rootCategory != category) { GSEbayCategory *parent = category; diff --git a/test/Analysis/retain-release-gc-only.m b/test/Analysis/retain-release-gc-only.m index cbf00a27721d8..ee1a6b4b782e0 100644 --- a/test/Analysis/retain-release-gc-only.m +++ b/test/Analysis/retain-release-gc-only.m @@ -113,6 +113,7 @@ NSFastEnumerationState; @end @interface NSAutoreleasePool : NSObject { } - (void)drain; +- (id)init; @end extern NSString * const NSBundleDidLoadNotification; typedef double NSTimeInterval; @interface NSDate : NSObject <NSCopying, NSCoding> - (NSTimeInterval)timeIntervalSinceReferenceDate; diff --git a/test/Analysis/retain-release-path-notes-gc.m b/test/Analysis/retain-release-path-notes-gc.m new file mode 100644 index 0000000000000..21d314fa15418 --- /dev/null +++ b/test/Analysis/retain-release-path-notes-gc.m @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease -analyzer-store=basic -analyzer-output=text -fobjc-gc-only -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease -analyzer-store=region -analyzer-output=text -fobjc-gc-only -verify %s + +/*** +This file is for testing the path-sensitive notes for retain/release errors. +Its goal is to have simple branch coverage of any path-based diagnostics, +not to actually check all possible retain/release errors. + +This file is for notes that only appear in a GC-enabled analysis. +Non-specific and ref-count-only notes should go in retain-release-path-notes.m. +***/ + +@interface NSObject ++ (id)alloc; +- (id)init; +- (void)dealloc; + +- (Class)class; + +- (id)retain; +- (void)release; +- (void)autorelease; +@end + +@interface Foo : NSObject +- (id)methodWithValue; +@property(retain) id propertyValue; +@end + +typedef struct CFType *CFTypeRef; +CFTypeRef CFRetain(CFTypeRef); +void CFRelease(CFTypeRef); + +id NSMakeCollectable(CFTypeRef); +CFTypeRef CFMakeCollectable(CFTypeRef); + +CFTypeRef CFCreateSomething(); +CFTypeRef CFGetSomething(); + + +void creationViaCFCreate () { + CFTypeRef leaked = CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void makeCollectable () { + CFTypeRef leaked = CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected}} + CFRetain(leaked); // expected-note{{Reference count incremented. The object now has a +2 retain count}} + CFMakeCollectable(leaked); // expected-note{{In GC mode a call to 'CFMakeCollectable' decrements an object's retain count and registers the object with the garbage collector. An object must have a 0 retain count to be garbage collected. After this call its retain count is +1.}} + NSMakeCollectable(leaked); // expected-note{{In GC mode a call to 'NSMakeCollectable' decrements an object's retain count and registers the object with the garbage collector. Since it now has a 0 retain count the object can be automatically collected by the garbage collector}} + CFRetain(leaked); // expected-note{{Reference count incremented. The object now has a +1 retain count. The object is not eligible for garbage collection until the retain count reaches 0 again.}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void retainReleaseIgnored () { + id object = [[NSObject alloc] init]; // expected-note{{Method returns an Objective-C object with a +0 retain count}} + [object retain]; // expected-note{{In GC mode the 'retain' message has no effect}} + [object release]; // expected-note{{In GC mode the 'release' message has no effect}} + [object autorelease]; // expected-note{{In GC mode an 'autorelease' has no effect}} + CFRelease((CFTypeRef)object); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} +} + +@implementation Foo (FundamentalRuleUnderGC) +- (id)getViolation { + id object = (id) CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected.}} + return object; // expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'object' and returned from method 'getViolation' is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector}} +} + +- (id)copyViolation { + id object = (id) CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected.}} + return object; // expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'object' and returned from method 'copyViolation' is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector}} +} +@end + diff --git a/test/Analysis/retain-release-path-notes.m b/test/Analysis/retain-release-path-notes.m new file mode 100644 index 0000000000000..bac0afb3f7abc --- /dev/null +++ b/test/Analysis/retain-release-path-notes.m @@ -0,0 +1,123 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease -analyzer-store=basic -analyzer-output=text -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease -analyzer-store=region -analyzer-output=text -verify %s + +/*** +This file is for testing the path-sensitive notes for retain/release errors. +Its goal is to have simple branch coverage of any path-based diagnostics, +not to actually check all possible retain/release errors. + +This file includes notes that only appear in a ref-counted analysis. +GC-specific notes should go in retain-release-path-notes-gc.m. +***/ + +@interface NSObject ++ (id)alloc; +- (id)init; +- (void)dealloc; + +- (Class)class; + +- (id)retain; +- (void)release; +- (void)autorelease; +@end + +@interface Foo : NSObject +- (id)methodWithValue; +@property(retain) id propertyValue; +@end + +typedef struct CFType *CFTypeRef; +CFTypeRef CFRetain(CFTypeRef); +void CFRelease(CFTypeRef); + +id NSMakeCollectable(CFTypeRef); +CFTypeRef CFMakeCollectable(CFTypeRef); + +CFTypeRef CFCreateSomething(); +CFTypeRef CFGetSomething(); + + +void creationViaAlloc () { + id leaked = [[NSObject alloc] init]; // expected-warning{{leak}} expected-note{{Method returns an Objective-C object with a +1 retain count}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void creationViaCFCreate () { + CFTypeRef leaked = CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void acquisitionViaMethod (Foo *foo) { + id leaked = [foo methodWithValue]; // expected-warning{{leak}} expected-note{{Method returns an Objective-C object with a +0 retain count}} + [leaked retain]; // expected-note{{Reference count incremented. The object now has a +1 retain count}} + [leaked retain]; // expected-note{{Reference count incremented. The object now has a +2 retain count}} + [leaked release]; // expected-note{{Reference count decremented. The object now has a +1 retain count}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void acquisitionViaProperty (Foo *foo) { + id leaked = foo.propertyValue; // expected-warning{{leak}} expected-note{{Property returns an Objective-C object with a +0 retain count}} + [leaked retain]; // expected-note{{Reference count incremented. The object now has a +1 retain count}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void acquisitionViaCFFunction () { + CFTypeRef leaked = CFGetSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object with a +0 retain count}} + CFRetain(leaked); // expected-note{{Reference count incremented. The object now has a +1 retain count}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void explicitDealloc () { + id object = [[NSObject alloc] init]; // expected-note{{Method returns an Objective-C object with a +1 retain count}} + [object dealloc]; // expected-note{{Object released by directly sending the '-dealloc' message}} + [object class]; // expected-warning{{Reference-counted object is used after it is released}} // expected-note{{Reference-counted object is used after it is released}} +} + +void implicitDealloc () { + id object = [[NSObject alloc] init]; // expected-note{{Method returns an Objective-C object with a +1 retain count}} + [object release]; // expected-note{{Object released}} + [object class]; // expected-warning{{Reference-counted object is used after it is released}} // expected-note{{Reference-counted object is used after it is released}} +} + +void overAutorelease () { + id object = [[NSObject alloc] init]; // expected-note{{Method returns an Objective-C object with a +1 retain count}} + [object autorelease]; // expected-note{{Object sent -autorelease message}} + [object autorelease]; // expected-note{{Object sent -autorelease message}} + return; // expected-warning{{Object sent -autorelease too many times}} expected-note{{Object over-autoreleased: object was sent -autorelease 2 times but the object has a +1 retain count}} +} + +void autoreleaseUnowned (Foo *foo) { + id object = foo.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}} + [object autorelease]; // expected-note{{Object sent -autorelease message}} + return; // expected-warning{{Object sent -autorelease too many times}} expected-note{{Object over-autoreleased: object was sent -autorelease but the object has a +0 retain count}} +} + +void makeCollectableIgnored () { + CFTypeRef leaked = CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count}} + CFMakeCollectable(leaked); // expected-note{{When GC is not enabled a call to 'CFMakeCollectable' has no effect on its argument}} + NSMakeCollectable(leaked); // expected-note{{When GC is not enabled a call to 'NSMakeCollectable' has no effect on its argument}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +CFTypeRef CFCopyRuleViolation () { + CFTypeRef object = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object with a +0 retain counte}} + return object; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} +} + +CFTypeRef CFGetRuleViolation () { + CFTypeRef object = CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain counte}} + return object; // expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'object' is return from a function whose name ('CFGetRuleViolation') does not contain 'Copy' or 'Create'. This violates the naming convention rules given the Memory Management Guide for Core Foundation}} +} + +@implementation Foo (FundamentalMemoryManagementRules) +- (id)copyViolation { + id result = self.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}} + return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} +} + +- (id)getViolation { + id result = [[Foo alloc] init]; // expected-warning{{leak}} expected-note{{Method returns an Objective-C object with a +1 retain count}} + return result; // expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'result' is returned from a method whose name ('getViolation') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'. This violates the naming convention rules given in the Memory Management Guide for Cocoa}} +} +@end diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m index 7af14f129c3f6..71ae756cf0454 100644 --- a/test/Analysis/retain-release.m +++ b/test/Analysis/retain-release.m @@ -116,6 +116,7 @@ typedef struct _NSZone NSZone; - (id)retain; - (oneway void)release; - (id)autorelease; +- (id)init; @end @protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end @protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; @end @protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @@ -517,7 +518,7 @@ void f17(int x, CFTypeRef p) { @implementation TestReturnNotOwnedWhenExpectedOwned - (NSString*)newString { NSString *s = [NSString stringWithUTF8String:"hello"]; - return s; // expected-warning{{Object with +0 retain counts returned to caller where a +1 (owning) retain count is expected}} + return s; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} } @end @@ -735,7 +736,7 @@ typedef CFTypeRef OtherRef; - (id)initReturningNewClassBad2 { [self release]; self = [[RDar6320065Subclass alloc] init]; - return [self autorelease]; // expected-warning{{Object with +0 retain counts returned to caller where a +1 (owning) retain count is expected}} + return [self autorelease]; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} } @end @@ -1302,7 +1303,7 @@ CFDateRef returnsRetainedCFDate() { } - (CFDateRef) newCFRetainedAsCFNoAttr { - return (CFDateRef)[(id)[self returnsCFRetainedAsCF] autorelease]; // expected-warning{{Object with +0 retain counts returned to caller where a +1 (owning) retain count is expected}} + return (CFDateRef)[(id)[self returnsCFRetainedAsCF] autorelease]; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} } - (NSDate*) alsoReturnsRetained { @@ -1445,7 +1446,7 @@ static void rdar_8724287(CFErrorRef error) while (error_to_dump != ((void*)0)) { CFDictionaryRef info; - info = CFErrorCopyUserInfo(error_to_dump); // expected-warning{{Potential leak of an object allocated on line 1448 and stored into 'info'}} + info = CFErrorCopyUserInfo(error_to_dump); // expected-warning{{Potential leak of an object allocated on line 1449 and stored into 'info'}} if (info != ((void*)0)) { } @@ -1461,3 +1462,81 @@ extern void rdar_9234108_helper(void *key, void * CF_CONSUMED value); void rdar_9234108() { rdar_9234108_helper(0, CFStringCreate()); } + +// <rdar://problem/9726279> - Make sure that objc_method_family works +// to override naming conventions. +struct TwoDoubles { + double one; + double two; +}; +typedef struct TwoDoubles TwoDoubles; + +@interface NSValue (Mine) +- (id)_prefix_initWithTwoDoubles:(TwoDoubles)twoDoubles __attribute__((objc_method_family(init))); +@end + +@implementation NSValue (Mine) +- (id)_prefix_initWithTwoDoubles:(TwoDoubles)twoDoubles +{ + return [self init]; +} +@end + +void rdar9726279() { + TwoDoubles twoDoubles = { 0.0, 0.0 }; + NSValue *value = [[NSValue alloc] _prefix_initWithTwoDoubles:twoDoubles]; + [value release]; +} + +// <rdar://problem/9732321> +// Test camelcase support for CF conventions. While Core Foundation APIs +// don't use camel casing, other code is allowed to use it. +CFArrayRef camelcase_create_1() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef camelcase_createno() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning {{leak}} +} + +CFArrayRef camelcase_copy() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef camelcase_copying() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning {{leak}} +} + +CFArrayRef copyCamelCase() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef __copyCamelCase() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef __createCamelCase() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef camel_create() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + + +CFArrayRef camel_creat() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning {{leak}} +} + +CFArrayRef camel_copy() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef camel_copyMachine() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef camel_copymachine() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning {{leak}} +} + diff --git a/test/Analysis/retain-release.mm b/test/Analysis/retain-release.mm index 0faeb1a2a3005..bdc3dc03964a4 100644 --- a/test/Analysis/retain-release.mm +++ b/test/Analysis/retain-release.mm @@ -121,6 +121,7 @@ typedef struct _NSZone NSZone; + (id)allocWithZone:(NSZone *)zone; + (id)alloc; - (void)dealloc; +- (id)init; @end @interface NSObject (NSCoderMethods) - (id)awakeAfterUsingCoder:(NSCoder *)aDecoder; diff --git a/test/Analysis/string-fail.c b/test/Analysis/string-fail.c new file mode 100644 index 0000000000000..64ac504d6d805 --- /dev/null +++ b/test/Analysis/string-fail.c @@ -0,0 +1,113 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,unix.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s +// XFAIL: * + +// This file is for tests that may eventually go into string.c, or may be +// deleted outright. At one point these tests passed, but only because we +// weren't correctly modelling the behavior of the relevant string functions. +// The tests aren't incorrect, but require the analyzer to be smarter about +// conjured values than it currently is. + +//===----------------------------------------------------------------------=== +// Declarations +//===----------------------------------------------------------------------=== + +// Some functions are so similar to each other that they follow the same code +// path, such as memcpy and __memcpy_chk, or memcmp and bcmp. If VARIANT is +// defined, make sure to use the variants instead to make sure they are still +// checked by the analyzer. + +// Some functions are implemented as builtins. These should be #defined as +// BUILTIN(f), which will prepend "__builtin_" if USE_BUILTINS is defined. + +// Functions that have variants and are also available as builtins should be +// declared carefully! See memcpy() for an example. + +#ifdef USE_BUILTINS +# define BUILTIN(f) __builtin_ ## f +#else /* USE_BUILTINS */ +# define BUILTIN(f) f +#endif /* USE_BUILTINS */ + +#define NULL 0 +typedef typeof(sizeof(int)) size_t; + + +//===----------------------------------------------------------------------=== +// strnlen() +//===----------------------------------------------------------------------=== + +#define strnlen BUILTIN(strnlen) +size_t strnlen(const char *s, size_t maxlen); + +void strnlen_liveness(const char *x) { + if (strnlen(x, 10) < 5) + return; + if (strnlen(x, 10) < 5) + (void)*(char*)0; // no-warning +} + +void strnlen_subregion() { + struct two_stringsn { char a[2], b[2]; }; + extern void use_two_stringsn(struct two_stringsn *); + + struct two_stringsn z; + use_two_stringsn(&z); + + size_t a = strnlen(z.a, 10); + z.b[0] = 5; + size_t b = strnlen(z.a, 10); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + use_two_stringsn(&z); + + size_t c = strnlen(z.a, 10); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +extern void use_stringn(char *); +void strnlen_argument(char *x) { + size_t a = strnlen(x, 10); + size_t b = strnlen(x, 10); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + use_stringn(x); + + size_t c = strnlen(x, 10); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +extern char global_strn[]; +void strnlen_global() { + size_t a = strnlen(global_strn, 10); + size_t b = strnlen(global_strn, 10); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + // Call a function with unknown effects, which should invalidate globals. + use_stringn(0); + + size_t c = strnlen(global_strn, 10); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +void strnlen_indirect(char *x) { + size_t a = strnlen(x, 10); + char *p = x; + char **p2 = &p; + size_t b = strnlen(x, 10); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + extern void use_stringn_ptr(char*const*); + use_stringn_ptr(p2); + + size_t c = strnlen(x, 10); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} diff --git a/test/Analysis/string.c b/test/Analysis/string.c index e6baf51c474cf..dc97e1a119e01 100644 --- a/test/Analysis/string.c +++ b/test/Analysis/string.c @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,cplusplus.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,cplusplus.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,cplusplus.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,unix.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,unix.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,unix.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s //===----------------------------------------------------------------------=== // Declarations @@ -55,16 +55,16 @@ void strlen_constant2(char x) { } size_t strlen_null() { - return strlen(0); // expected-warning{{Null pointer argument in call to byte string function}} + return strlen(0); // expected-warning{{Null pointer argument in call to string length function}} } size_t strlen_fn() { - return strlen((char*)&strlen_fn); // expected-warning{{Argument to byte string function is the address of the function 'strlen_fn', which is not a null-terminated string}} + return strlen((char*)&strlen_fn); // expected-warning{{Argument to string length function is the address of the function 'strlen_fn', which is not a null-terminated string}} } size_t strlen_nonloc() { label: - return strlen((char*)&&label); // expected-warning{{Argument to byte string function is the address of the label 'label', which is not a null-terminated string}} + return strlen((char*)&&label); // expected-warning{{Argument to string length function is the address of the label 'label', which is not a null-terminated string}} } void strlen_subregion() { @@ -143,24 +143,23 @@ void strlen_liveness(const char *x) { // strnlen() //===----------------------------------------------------------------------=== -#define strnlen BUILTIN(strnlen) size_t strnlen(const char *s, size_t maxlen); void strnlen_constant0() { if (strnlen("123", 10) != 3) - (void)*(char*)0; // no-warning + (void)*(char*)0; // expected-warning{{never executed}} } void strnlen_constant1() { const char *a = "123"; if (strnlen(a, 10) != 3) - (void)*(char*)0; // no-warning + (void)*(char*)0; // expected-warning{{never executed}} } void strnlen_constant2(char x) { char a[] = "123"; if (strnlen(a, 10) != 3) - (void)*(char*)0; // no-warning + (void)*(char*)0; // expected-warning{{never executed}} a[0] = x; if (strnlen(a, 10) != 3) (void)*(char*)0; // expected-warning{{null}} @@ -168,107 +167,90 @@ void strnlen_constant2(char x) { void strnlen_constant4() { if (strnlen("123456", 3) != 3) - (void)*(char*)0; // no-warning + (void)*(char*)0; // expected-warning{{never executed}} } void strnlen_constant5() { const char *a = "123456"; if (strnlen(a, 3) != 3) - (void)*(char*)0; // no-warning + (void)*(char*)0; // expected-warning{{never executed}} } void strnlen_constant6(char x) { char a[] = "123456"; if (strnlen(a, 3) != 3) - (void)*(char*)0; // no-warning + (void)*(char*)0; // expected-warning{{never executed}} a[0] = x; if (strnlen(a, 3) != 3) (void)*(char*)0; // expected-warning{{null}} } size_t strnlen_null() { - return strnlen(0, 3); // expected-warning{{Null pointer argument in call to byte string function}} + return strnlen(0, 3); // expected-warning{{Null pointer argument in call to string length function}} } size_t strnlen_fn() { - return strnlen((char*)&strlen_fn, 3); // expected-warning{{Argument to byte string function is the address of the function 'strlen_fn', which is not a null-terminated string}} + return strnlen((char*)&strlen_fn, 3); // expected-warning{{Argument to string length function is the address of the function 'strlen_fn', which is not a null-terminated string}} } size_t strnlen_nonloc() { label: - return strnlen((char*)&&label, 3); // expected-warning{{Argument to byte string function is the address of the label 'label', which is not a null-terminated string}} + return strnlen((char*)&&label, 3); // expected-warning{{Argument to string length function is the address of the label 'label', which is not a null-terminated string}} } -void strnlen_subregion() { - struct two_stringsn { char a[2], b[2]; }; - extern void use_two_stringsn(struct two_stringsn *); - - struct two_stringsn z; - use_two_stringsn(&z); - - size_t a = strnlen(z.a, 10); - z.b[0] = 5; - size_t b = strnlen(z.a, 10); - if (a == 0 && b != 0) +void strnlen_zero() { + if (strnlen("abc", 0) != 0) (void)*(char*)0; // expected-warning{{never executed}} + if (strnlen(NULL, 0) != 0) // no-warning + (void)*(char*)0; // no-warning +} + +size_t strnlen_compound_literal() { + // This used to crash because we don't model the string lengths of + // compound literals. + return strnlen((char[]) { 'a', 'b', 0 }, 1); +} - use_two_stringsn(&z); +size_t strnlen_unknown_limit(float f) { + // This used to crash because we don't model the integer values of floats. + return strnlen("abc", (int)f); +} - size_t c = strnlen(z.a, 10); - if (a == 0 && c != 0) +void strnlen_is_not_strlen(char *x) { + if (strnlen(x, 10) != strlen(x)) (void)*(char*)0; // expected-warning{{null}} } -extern void use_stringn(char *); -void strnlen_argument(char *x) { - size_t a = strnlen(x, 10); - size_t b = strnlen(x, 10); - if (a == 0 && b != 0) +void strnlen_at_limit(char *x) { + size_t len = strnlen(x, 10); + if (len > 10) (void)*(char*)0; // expected-warning{{never executed}} - - use_stringn(x); - - size_t c = strnlen(x, 10); - if (a == 0 && c != 0) - (void)*(char*)0; // expected-warning{{null}} + if (len == 10) + (void)*(char*)0; // expected-warning{{null}} } -extern char global_strn[]; -void strnlen_global() { - size_t a = strnlen(global_strn, 10); - size_t b = strnlen(global_strn, 10); - if (a == 0 && b != 0) +void strnlen_less_than_limit(char *x) { + size_t len = strnlen(x, 10); + if (len > 10) (void)*(char*)0; // expected-warning{{never executed}} - - // Call a function with unknown effects, which should invalidate globals. - use_stringn(0); - - size_t c = strnlen(global_str, 10); - if (a == 0 && c != 0) - (void)*(char*)0; // expected-warning{{null}} + if (len < 10) + (void)*(char*)0; // expected-warning{{null}} } -void strnlen_indirect(char *x) { - size_t a = strnlen(x, 10); - char *p = x; - char **p2 = &p; - size_t b = strnlen(x, 10); - if (a == 0 && b != 0) +void strnlen_at_actual(size_t limit) { + size_t len = strnlen("abc", limit); + if (len > 3) (void)*(char*)0; // expected-warning{{never executed}} - - extern void use_stringn_ptr(char*const*); - use_stringn_ptr(p2); - - size_t c = strnlen(x, 10); - if (a == 0 && c != 0) + if (len == 3) (void)*(char*)0; // expected-warning{{null}} } -void strnlen_liveness(const char *x) { - if (strnlen(x, 10) < 5) - return; - if (strnlen(x, 10) < 5) - (void)*(char*)0; // no-warning +void strnlen_less_than_actual(size_t limit) { + size_t len = strnlen("abc", limit); + if (len > 3) + (void)*(char*)0; // expected-warning{{never executed}} + if (len < 3) + (void)*(char*)0; // expected-warning{{null}} } //===----------------------------------------------------------------------=== @@ -291,15 +273,15 @@ char *strcpy(char *restrict s1, const char *restrict s2); void strcpy_null_dst(char *x) { - strcpy(NULL, x); // expected-warning{{Null pointer argument in call to byte string function}} + strcpy(NULL, x); // expected-warning{{Null pointer argument in call to string copy function}} } void strcpy_null_src(char *x) { - strcpy(x, NULL); // expected-warning{{Null pointer argument in call to byte string function}} + strcpy(x, NULL); // expected-warning{{Null pointer argument in call to string copy function}} } void strcpy_fn(char *x) { - strcpy(x, (char*)&strcpy_fn); // expected-warning{{Argument to byte string function is the address of the function 'strcpy_fn', which is not a null-terminated string}} + strcpy(x, (char*)&strcpy_fn); // expected-warning{{Argument to string copy function is the address of the function 'strcpy_fn', which is not a null-terminated string}} } void strcpy_effects(char *x, char *y) { @@ -318,7 +300,7 @@ void strcpy_effects(char *x, char *y) { void strcpy_overflow(char *y) { char x[4]; if (strlen(y) == 4) - strcpy(x, y); // expected-warning{{Byte string function overflows destination buffer}} + strcpy(x, y); // expected-warning{{String copy function overflows destination buffer}} } void strcpy_no_overflow(char *y) { @@ -362,7 +344,7 @@ void stpcpy_effect(char *x, char *y) { void stpcpy_overflow(char *y) { char x[4]; if (strlen(y) == 4) - stpcpy(x, y); // expected-warning{{Byte string function overflows destination buffer}} + stpcpy(x, y); // expected-warning{{String copy function overflows destination buffer}} } void stpcpy_no_overflow(char *y) { @@ -391,15 +373,15 @@ char *strcat(char *restrict s1, const char *restrict s2); void strcat_null_dst(char *x) { - strcat(NULL, x); // expected-warning{{Null pointer argument in call to byte string function}} + strcat(NULL, x); // expected-warning{{Null pointer argument in call to string copy function}} } void strcat_null_src(char *x) { - strcat(x, NULL); // expected-warning{{Null pointer argument in call to byte string function}} + strcat(x, NULL); // expected-warning{{Null pointer argument in call to string copy function}} } void strcat_fn(char *x) { - strcat(x, (char*)&strcat_fn); // expected-warning{{Argument to byte string function is the address of the function 'strcat_fn', which is not a null-terminated string}} + strcat(x, (char*)&strcat_fn); // expected-warning{{Argument to string copy function is the address of the function 'strcat_fn', which is not a null-terminated string}} } void strcat_effects(char *y) { @@ -415,27 +397,24 @@ void strcat_effects(char *y) { if ((int)strlen(x) != (orig_len + strlen(y))) (void)*(char*)0; // no-warning - - if (a != x[0]) - (void)*(char*)0; // expected-warning{{null}} } void strcat_overflow_0(char *y) { char x[4] = "12"; if (strlen(y) == 4) - strcat(x, y); // expected-warning{{Byte string function overflows destination buffer}} + strcat(x, y); // expected-warning{{String copy function overflows destination buffer}} } void strcat_overflow_1(char *y) { char x[4] = "12"; if (strlen(y) == 3) - strcat(x, y); // expected-warning{{Byte string function overflows destination buffer}} + strcat(x, y); // expected-warning{{String copy function overflows destination buffer}} } void strcat_overflow_2(char *y) { char x[4] = "12"; if (strlen(y) == 2) - strcat(x, y); // expected-warning{{Byte string function overflows destination buffer}} + strcat(x, y); // expected-warning{{String copy function overflows destination buffer}} } void strcat_no_overflow(char *y) { @@ -444,6 +423,136 @@ void strcat_no_overflow(char *y) { strcat(x, y); // no-warning } +void strcat_symbolic_dst_length(char *dst) { + strcat(dst, "1234"); + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning +} + +void strcat_symbolic_src_length(char *src) { + char dst[8] = "1234"; + strcat(dst, src); + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning +} + +void strcat_unknown_src_length(char *src, int offset) { + char dst[8] = "1234"; + strcat(dst, &src[offset]); + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning +} + +// There is no strcat_unknown_dst_length because if we can't get a symbolic +// length for the "before" strlen, we won't be able to set one for "after". + +void strcat_too_big(char *dst, char *src) { + if (strlen(dst) != (((size_t)0) - 2)) + return; + if (strlen(src) != 2) + return; + strcat(dst, src); // expected-warning{{This expression will create a string whose length is too big to be represented as a size_t}} +} + + +//===----------------------------------------------------------------------=== +// strncpy() +//===----------------------------------------------------------------------=== + +#ifdef VARIANT + +#define __strncpy_chk BUILTIN(__strncpy_chk) +char *__strncpy_chk(char *restrict s1, const char *restrict s2, size_t n, size_t destlen); + +#define strncpy(a,b,n) __strncpy_chk(a,b,n,(size_t)-1) + +#else /* VARIANT */ + +#define strncpy BUILTIN(strncpy) +char *strncpy(char *restrict s1, const char *restrict s2, size_t n); + +#endif /* VARIANT */ + + +void strncpy_null_dst(char *x) { + strncpy(NULL, x, 5); // expected-warning{{Null pointer argument in call to string copy function}} +} + +void strncpy_null_src(char *x) { + strncpy(x, NULL, 5); // expected-warning{{Null pointer argument in call to string copy function}} +} + +void strncpy_fn(char *x) { + strncpy(x, (char*)&strcpy_fn, 5); // expected-warning{{Argument to string copy function is the address of the function 'strcpy_fn', which is not a null-terminated string}} +} + +void strncpy_effects(char *x, char *y) { + char a = x[0]; + + if (strncpy(x, y, 5) != x) + (void)*(char*)0; // no-warning + + if (strlen(x) != strlen(y)) + (void)*(char*)0; // expected-warning{{null}} + + if (a != x[0]) + (void)*(char*)0; // expected-warning{{null}} +} + +void strncpy_overflow(char *y) { + char x[4]; + if (strlen(y) == 4) + strncpy(x, y, 5); // expected-warning{{Size argument is greater than the length of the destination buffer}} +} + +void strncpy_no_overflow(char *y) { + char x[4]; + if (strlen(y) == 3) + strncpy(x, y, 5); // expected-warning{{Size argument is greater than the length of the destination buffer}} +} + +void strncpy_no_overflow2(char *y, int n) { + if (n <= 4) + return; + + char x[4]; + if (strlen(y) == 3) + strncpy(x, y, n); // expected-warning{{Size argument is greater than the length of the destination buffer}} +} + +void strncpy_truncate(char *y) { + char x[4]; + if (strlen(y) == 4) + strncpy(x, y, 3); // no-warning +} + +void strncpy_no_truncate(char *y) { + char x[4]; + if (strlen(y) == 3) + strncpy(x, y, 3); // no-warning +} + +void strncpy_exactly_matching_buffer(char *y) { + char x[4]; + strncpy(x, y, 4); // no-warning + + // strncpy does not null-terminate, so we have no idea what the strlen is + // after this. + if (strlen(x) > 4) + (void)*(int*)0; // expected-warning{{null}} +} + +void strncpy_exactly_matching_buffer2(char *y) { + if (strlen(y) >= 4) + return; + + char x[4]; + strncpy(x, y, 4); // no-warning + + // This time, we know that y fits in x anyway. + if (strlen(x) > 3) + (void)*(int*)0; // no-warning +} //===----------------------------------------------------------------------=== // strncat() @@ -465,15 +574,15 @@ char *strncat(char *restrict s1, const char *restrict s2, size_t n); void strncat_null_dst(char *x) { - strncat(NULL, x, 4); // expected-warning{{Null pointer argument in call to byte string function}} + strncat(NULL, x, 4); // expected-warning{{Null pointer argument in call to string copy function}} } void strncat_null_src(char *x) { - strncat(x, NULL, 4); // expected-warning{{Null pointer argument in call to byte string function}} + strncat(x, NULL, 4); // expected-warning{{Null pointer argument in call to string copy function}} } void strncat_fn(char *x) { - strncat(x, (char*)&strncat_fn, 4); // expected-warning{{Argument to byte string function is the address of the function 'strncat_fn', which is not a null-terminated string}} + strncat(x, (char*)&strncat_fn, 4); // expected-warning{{Argument to string copy function is the address of the function 'strncat_fn', which is not a null-terminated string}} } void strncat_effects(char *y) { @@ -489,33 +598,30 @@ void strncat_effects(char *y) { if (strlen(x) != orig_len + strlen(y)) (void)*(char*)0; // no-warning - - if (a != x[0]) - (void)*(char*)0; // expected-warning{{null}} } void strncat_overflow_0(char *y) { char x[4] = "12"; if (strlen(y) == 4) - strncat(x, y, strlen(y)); // expected-warning{{Byte string function overflows destination buffer}} + strncat(x, y, strlen(y)); // expected-warning{{Size argument is greater than the free space in the destination buffer}} } void strncat_overflow_1(char *y) { char x[4] = "12"; if (strlen(y) == 3) - strncat(x, y, strlen(y)); // expected-warning{{Byte string function overflows destination buffer}} + strncat(x, y, strlen(y)); // expected-warning{{Size argument is greater than the free space in the destination buffer}} } void strncat_overflow_2(char *y) { char x[4] = "12"; if (strlen(y) == 2) - strncat(x, y, strlen(y)); // expected-warning{{Byte string function overflows destination buffer}} + strncat(x, y, strlen(y)); // expected-warning{{Size argument is greater than the free space in the destination buffer}} } void strncat_overflow_3(char *y) { char x[4] = "12"; if (strlen(y) == 4) - strncat(x, y, 2); // expected-warning{{Byte string function overflows destination buffer}} + strncat(x, y, 2); // expected-warning{{Size argument is greater than the free space in the destination buffer}} } void strncat_no_overflow_1(char *y) { char x[5] = "12"; @@ -529,12 +635,69 @@ void strncat_no_overflow_2(char *y) { strncat(x, y, 1); // no-warning } +void strncat_symbolic_dst_length(char *dst) { + strncat(dst, "1234", 5); + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning +} + +void strncat_symbolic_src_length(char *src) { + char dst[8] = "1234"; + strncat(dst, src, 3); + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning + + char dst2[8] = "1234"; + strncat(dst2, src, 4); // expected-warning{{Size argument is greater than the free space in the destination buffer}} +} + +void strncat_unknown_src_length(char *src, int offset) { + char dst[8] = "1234"; + strncat(dst, &src[offset], 3); + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning + + char dst2[8] = "1234"; + strncat(dst2, &src[offset], 4); // expected-warning{{Size argument is greater than the free space in the destination buffer}} +} + +// There is no strncat_unknown_dst_length because if we can't get a symbolic +// length for the "before" strlen, we won't be able to set one for "after". + +void strncat_symbolic_limit(unsigned limit) { + char dst[6] = "1234"; + char src[] = "567"; + strncat(dst, src, limit); // no-warning + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning + if (strlen(dst) == 4) + (void)*(char*)0; // expected-warning{{null}} +} + +void strncat_unknown_limit(float limit) { + char dst[6] = "1234"; + char src[] = "567"; + strncat(dst, src, (size_t)limit); // no-warning + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning + if (strlen(dst) == 4) + (void)*(char*)0; // expected-warning{{null}} +} + +void strncat_too_big(char *dst, char *src) { + if (strlen(dst) != (((size_t)0) - 2)) + return; + if (strlen(src) != 2) + return; + strncat(dst, src, 2); // expected-warning{{This expression will create a string whose length is too big to be represented as a size_t}} +} + //===----------------------------------------------------------------------=== // strcmp() //===----------------------------------------------------------------------=== #define strcmp BUILTIN(strcmp) -int strcmp(const char *restrict s1, const char *restrict s2); +int strcmp(const char * s1, const char * s2); void strcmp_constant0() { if (strcmp("123", "123") != 0) @@ -577,13 +740,13 @@ void strcmp_2() { void strcmp_null_0() { char *x = NULL; char *y = "123"; - strcmp(x, y); // expected-warning{{Null pointer argument in call to byte string function}} + strcmp(x, y); // expected-warning{{Null pointer argument in call to string comparison function}} } void strcmp_null_1() { char *x = "123"; char *y = NULL; - strcmp(x, y); // expected-warning{{Null pointer argument in call to byte string function}} + strcmp(x, y); // expected-warning{{Null pointer argument in call to string comparison function}} } void strcmp_diff_length_0() { @@ -614,12 +777,22 @@ void strcmp_diff_length_3() { (void)*(char*)0; // no-warning } +void strcmp_embedded_null () { + if (strcmp("\0z", "\0y") != 0) + (void)*(char*)0; // no-warning +} + +void strcmp_unknown_arg (char *unknown) { + if (strcmp(unknown, unknown) != 0) + (void)*(char*)0; // no-warning +} + //===----------------------------------------------------------------------=== // strncmp() //===----------------------------------------------------------------------=== #define strncmp BUILTIN(strncmp) -int strncmp(const char *restrict s1, const char *restrict s2, size_t n); +int strncmp(const char *s1, const char *s2, size_t n); void strncmp_constant0() { if (strncmp("123", "123", 3) != 0) @@ -662,13 +835,13 @@ void strncmp_2() { void strncmp_null_0() { char *x = NULL; char *y = "123"; - strncmp(x, y, 3); // expected-warning{{Null pointer argument in call to byte string function}} + strncmp(x, y, 3); // expected-warning{{Null pointer argument in call to string comparison function}} } void strncmp_null_1() { char *x = "123"; char *y = NULL; - strncmp(x, y, 3); // expected-warning{{Null pointer argument in call to byte string function}} + strncmp(x, y, 3); // expected-warning{{Null pointer argument in call to string comparison function}} } void strncmp_diff_length_0() { @@ -720,12 +893,17 @@ void strncmp_diff_length_6() { (void)*(char*)0; // no-warning } +void strncmp_embedded_null () { + if (strncmp("ab\0zz", "ab\0yy", 4) != 0) + (void)*(char*)0; // no-warning +} + //===----------------------------------------------------------------------=== // strcasecmp() //===----------------------------------------------------------------------=== #define strcasecmp BUILTIN(strcasecmp) -int strcasecmp(const char *restrict s1, const char *restrict s2); +int strcasecmp(const char *s1, const char *s2); void strcasecmp_constant0() { if (strcasecmp("abc", "Abc") != 0) @@ -768,13 +946,13 @@ void strcasecmp_2() { void strcasecmp_null_0() { char *x = NULL; char *y = "123"; - strcasecmp(x, y); // expected-warning{{Null pointer argument in call to byte string function}} + strcasecmp(x, y); // expected-warning{{Null pointer argument in call to string comparison function}} } void strcasecmp_null_1() { char *x = "123"; char *y = NULL; - strcasecmp(x, y); // expected-warning{{Null pointer argument in call to byte string function}} + strcasecmp(x, y); // expected-warning{{Null pointer argument in call to string comparison function}} } void strcasecmp_diff_length_0() { @@ -805,12 +983,17 @@ void strcasecmp_diff_length_3() { (void)*(char*)0; // no-warning } +void strcasecmp_embedded_null () { + if (strcasecmp("ab\0zz", "ab\0yy") != 0) + (void)*(char*)0; // no-warning +} + //===----------------------------------------------------------------------=== // strncasecmp() //===----------------------------------------------------------------------=== #define strncasecmp BUILTIN(strncasecmp) -int strncasecmp(const char *restrict s1, const char *restrict s2, size_t n); +int strncasecmp(const char *s1, const char *s2, size_t n); void strncasecmp_constant0() { if (strncasecmp("abc", "Abc", 3) != 0) @@ -853,13 +1036,13 @@ void strncasecmp_2() { void strncasecmp_null_0() { char *x = NULL; char *y = "123"; - strncasecmp(x, y, 3); // expected-warning{{Null pointer argument in call to byte string function}} + strncasecmp(x, y, 3); // expected-warning{{Null pointer argument in call to string comparison function}} } void strncasecmp_null_1() { char *x = "123"; char *y = NULL; - strncasecmp(x, y, 3); // expected-warning{{Null pointer argument in call to byte string function}} + strncasecmp(x, y, 3); // expected-warning{{Null pointer argument in call to string comparison function}} } void strncasecmp_diff_length_0() { @@ -910,3 +1093,8 @@ void strncasecmp_diff_length_6() { if (strncasecmp(x, y, 3) != 1) (void)*(char*)0; // no-warning } + +void strncasecmp_embedded_null () { + if (strncasecmp("ab\0zz", "ab\0yy", 4) != 0) + (void)*(char*)0; // no-warning +} diff --git a/test/Analysis/uninit-ps-rdar6145427.m b/test/Analysis/uninit-ps-rdar6145427.m index f1794068d5e16..f4df91396a844 100644 --- a/test/Analysis/uninit-ps-rdar6145427.m +++ b/test/Analysis/uninit-ps-rdar6145427.m @@ -11,7 +11,10 @@ typedef struct _NSZone NSZone; @protocol NSObject - (BOOL)isEqual:(id)object; @end @protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end @protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end -@interface NSObject <NSObject> {} + (id)alloc; @end +@interface NSObject <NSObject> {} ++ (id)alloc; +- (id)init; +@end extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone); @interface NSValue : NSObject <NSCopying, NSCoding> - (void)getValue:(void *)value; @end @class NSString, NSData; diff --git a/test/Analysis/uninit-vals-ps-region.m b/test/Analysis/uninit-vals-ps-region.m index 1700f54dbfb08..c62818a12e0d7 100644 --- a/test/Analysis/uninit-vals-ps-region.m +++ b/test/Analysis/uninit-vals-ps-region.m @@ -67,3 +67,12 @@ void rdar_7780304() { b.x |= 1; // expected-warning{{The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage}} } + +// The flip side of PR10163 -- float arrays that are actually uninitialized +// (The main test is in uninit-vals.m) +void test_PR10163(float); +void PR10163 (void) { + float x[2]; + test_PR10163(x[1]); // expected-warning{{uninitialized value}} +} + diff --git a/test/Analysis/uninit-vals.m b/test/Analysis/uninit-vals.m index 2cd5e0c118487..4b7e6f42cf8f4 100644 --- a/test/Analysis/uninit-vals.m +++ b/test/Analysis/uninit-vals.m @@ -23,3 +23,12 @@ NSUInteger f8(A* x){ return n; } + + +// PR10163 -- don't warn for default-initialized float arrays. +// (An additional test is in uninit-vals-ps-region.m) +void test_PR10163(float); +void PR10163 (void) { + float x[2] = {0}; + test_PR10163(x[1]); // no-warning +} diff --git a/test/Analysis/variadic-method-types.m b/test/Analysis/variadic-method-types.m index 018956ab1b2d7..03c309a5004aa 100644 --- a/test/Analysis/variadic-method-types.m +++ b/test/Analysis/variadic-method-types.m @@ -73,13 +73,13 @@ void f(id a, id<P> b, C* c, C<P> *d, FooType fooType, BarType barType) { [NSDictionary dictionaryWithObjectsAndKeys:@"Foo", "Bar", nil]; // expected-warning {{Argument to 'NSDictionary' method 'dictionaryWithObjectsAndKeys:' should be an Objective-C pointer type, not 'char *'}} [NSSet setWithObjects:@"Foo", "Bar", nil]; // expected-warning {{Argument to 'NSSet' method 'setWithObjects:' should be an Objective-C pointer type, not 'char *'}} - [[[NSArray alloc] initWithObjects:@"Foo", "Bar", nil] autorelease]; // expected-warning {{Argument to method 'initWithObjects:' should be an Objective-C pointer type, not 'char *'}} - [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", "Bar", nil] autorelease]; // expected-warning {{Argument to method 'initWithObjectsAndKeys:' should be an Objective-C pointer type, not 'char *'}} + [[[NSArray alloc] initWithObjects:@"Foo", "Bar", nil] autorelease]; // expected-warning {{Argument to 'NSArray' method 'initWithObjects:' should be an Objective-C pointer type, not 'char *'}} + [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", "Bar", nil] autorelease]; // expected-warning {{Argument to 'NSDictionary' method 'initWithObjectsAndKeys:' should be an Objective-C pointer type, not 'char *'}} [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", (void*) 0, nil] autorelease]; // no-warning [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", kCGImageSourceShouldCache, nil] autorelease]; // no-warning [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", fooType, nil] autorelease]; // no-warning - [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", barType, nil] autorelease]; // expected-warning {{Argument to method 'initWithObjectsAndKeys:' should be an Objective-C pointer type, not 'BarType'}} - [[[NSSet alloc] initWithObjects:@"Foo", "Bar", nil] autorelease]; // expected-warning {{Argument to method 'initWithObjects:' should be an Objective-C pointer type, not 'char *'}} + [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", barType, nil] autorelease]; // expected-warning {{Argument to 'NSDictionary' method 'initWithObjectsAndKeys:' should be an Objective-C pointer type, not 'BarType'}} + [[[NSSet alloc] initWithObjects:@"Foo", "Bar", nil] autorelease]; // expected-warning {{Argument to 'NSSet' method 'initWithObjects:' should be an Objective-C pointer type, not 'char *'}} } // This previously crashed the variadic argument checker. |