2017-03-04 02:02:02 +08:00
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core.DivideZero,core.DynamicTypePropagation,osx.cocoa.IncompatibleMethodTypes -w -verify %s
|
2012-07-31 04:31:29 +08:00
|
|
|
|
|
|
|
#include "InlineObjCInstanceMethod.h"
|
|
|
|
|
2012-11-13 07:40:29 +08:00
|
|
|
typedef const struct __CFString * CFStringRef;
|
|
|
|
typedef const void * CFTypeRef;
|
|
|
|
extern CFTypeRef CFRetain(CFTypeRef cf);
|
|
|
|
extern void CFRelease(CFTypeRef cf);
|
|
|
|
extern CFStringRef getString(void);
|
|
|
|
|
2012-07-31 04:31:29 +08:00
|
|
|
// Method is defined in the parent; called through self.
|
|
|
|
@interface MyParent : NSObject
|
|
|
|
- (int)getInt;
|
2012-11-13 07:40:29 +08:00
|
|
|
- (const struct __CFString *) testCovariantReturnType __attribute__((cf_returns_retained));
|
2012-07-31 04:31:29 +08:00
|
|
|
@end
|
|
|
|
@implementation MyParent
|
|
|
|
- (int)getInt {
|
|
|
|
return 0;
|
|
|
|
}
|
2012-11-13 07:40:29 +08:00
|
|
|
|
|
|
|
- (CFStringRef) testCovariantReturnType __attribute__((cf_returns_retained)) {
|
|
|
|
CFStringRef Str = ((void*)0);
|
|
|
|
Str = getString();
|
|
|
|
if (Str) {
|
|
|
|
CFRetain(Str);
|
|
|
|
}
|
|
|
|
return Str;
|
|
|
|
}
|
|
|
|
|
2012-07-31 04:31:29 +08:00
|
|
|
@end
|
|
|
|
|
|
|
|
@interface MyClass : MyParent
|
|
|
|
@end
|
|
|
|
@implementation MyClass
|
|
|
|
- (int)testDynDispatchSelf {
|
|
|
|
int y = [self getInt];
|
|
|
|
return 5/y; // expected-warning {{Division by zero}}
|
|
|
|
}
|
|
|
|
|
2012-08-04 05:43:37 +08:00
|
|
|
// Get the dynamic type info from a cast (from id to MyClass*).
|
2012-07-31 04:31:29 +08:00
|
|
|
+ (int)testAllocInit {
|
|
|
|
MyClass *a = [[self alloc] init];
|
2012-08-04 05:43:37 +08:00
|
|
|
return 5/[a getInt]; // expected-warning {{Division by zero}}
|
2012-07-31 04:31:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method is called on inited object.
|
|
|
|
+ (int)testAllocInit2 {
|
|
|
|
MyClass *a = [[MyClass alloc] init];
|
2012-08-07 07:25:39 +08:00
|
|
|
return 5/[a getInt]; // expected-warning {{Division by zero}}
|
2012-07-31 04:31:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method is called on a parameter.
|
|
|
|
+ (int)testParam: (MyClass*) a {
|
|
|
|
return 5/[a getInt]; // expected-warning {{Division by zero}}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method is called on a parameter of unnown type.
|
|
|
|
+ (int)testParamUnknownType: (id) a {
|
|
|
|
return 5/[a getInt]; // no warning
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
// TODO: When method is inlined, the attribute reset should be visible.
|
|
|
|
@interface TestSettingAnAttributeInCallee : NSObject {
|
|
|
|
int _attribute;
|
|
|
|
}
|
|
|
|
- (void) method2;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation TestSettingAnAttributeInCallee
|
|
|
|
- (int) method1 {
|
|
|
|
[self method2];
|
|
|
|
return 5/_attribute; // expected-warning {{Division by zero}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) method2 {
|
|
|
|
_attribute = 0;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface TestSettingAnAttributeInCaller : NSObject {
|
|
|
|
int _attribute;
|
|
|
|
}
|
|
|
|
- (int) method2;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation TestSettingAnAttributeInCaller
|
|
|
|
- (void) method1 {
|
|
|
|
_attribute = 0;
|
|
|
|
[self method2];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int) method2 {
|
|
|
|
return 5/_attribute; // expected-warning {{Division by zero}}
|
|
|
|
}
|
2012-08-01 02:04:53 +08:00
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
// Don't crash if we don't know the receiver's region.
|
|
|
|
void randomlyMessageAnObject(MyClass *arr[], int i) {
|
|
|
|
(void)[arr[i] getInt];
|
[analyzer] Adjust the return type of an inlined devirtualized method call.
In C++, overriding virtual methods are allowed to specify a covariant
return type -- that is, if the return type of the base method is an
object pointer type (or reference type), the overriding method's return
type can be a pointer to a subclass of the original type. The analyzer
was failing to take this into account when devirtualizing a method call,
and anything that relied on the return value having the proper type later
would crash.
In Objective-C, overriding methods are allowed to specify ANY return type,
meaning we can NEVER be sure that devirtualizing will give us a "safe"
return value. Of course, a program that does this will most likely crash
at runtime, but the analyzer at least shouldn't crash.
The solution is to check and see if the function/method being inlined is
the function that static binding would have picked. If not, check that
the return value has the same type. If the types don't match, see if we
can fix it with a derived-to-base cast (the C++ case). If we can't,
return UnknownVal to avoid crashing later.
<rdar://problem/12409977>
llvm-svn: 165079
2012-10-03 09:08:35 +08:00
|
|
|
}
|
2012-10-04 00:00:32 +08:00
|
|
|
|
|
|
|
|
|
|
|
@interface EvilChild : MyParent
|
|
|
|
- (id)getInt;
|
2012-11-13 07:40:29 +08:00
|
|
|
- (const struct __CFString *) testCovariantReturnType __attribute__((cf_returns_retained));
|
2012-10-04 00:00:32 +08:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation EvilChild
|
|
|
|
- (id)getInt { // expected-warning {{types are incompatible}}
|
|
|
|
return self;
|
|
|
|
}
|
2012-11-13 07:40:29 +08:00
|
|
|
- (CFStringRef) testCovariantReturnType __attribute__((cf_returns_retained)) {
|
|
|
|
CFStringRef Str = ((void*)0);
|
|
|
|
Str = getString();
|
|
|
|
if (Str) {
|
|
|
|
CFRetain(Str);
|
|
|
|
}
|
|
|
|
return Str;
|
|
|
|
}
|
|
|
|
|
2012-10-04 00:00:32 +08:00
|
|
|
@end
|
|
|
|
|
|
|
|
int testNonCovariantReturnType() {
|
|
|
|
MyParent *obj = [[EvilChild alloc] init];
|
|
|
|
|
|
|
|
// Devirtualization allows us to directly call -[EvilChild getInt], but
|
|
|
|
// that returns an id, not an int. There is an off-by-default warning for
|
|
|
|
// this, -Woverriding-method-mismatch, and an on-by-default analyzer warning,
|
|
|
|
// osx.cocoa.IncompatibleMethodTypes. This code would probably crash at
|
|
|
|
// runtime, but at least the analyzer shouldn't crash.
|
|
|
|
int x = 1 + [obj getInt];
|
|
|
|
|
|
|
|
[obj release];
|
|
|
|
return 5/(x-1); // no-warning
|
|
|
|
}
|
2012-11-13 07:40:29 +08:00
|
|
|
|
|
|
|
int testCovariantReturnTypeNoErrorSinceTypesMatch() {
|
|
|
|
MyParent *obj = [[EvilChild alloc] init];
|
|
|
|
|
|
|
|
CFStringRef S = ((void*)0);
|
|
|
|
S = [obj testCovariantReturnType];
|
|
|
|
if (S)
|
|
|
|
CFRelease(S);
|
|
|
|
CFRelease(obj);
|
|
|
|
}
|