| 1 | // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config ipa=dynamic-bifurcate -verify %s |
| 2 | |
| 3 | #include "InlineObjCInstanceMethod.h" |
| 4 | |
| 5 | void clang_analyzer_eval(int); |
| 6 | |
| 7 | PublicSubClass2 *getObj(); |
| 8 | |
| 9 | @implementation PublicParent |
| 10 | - (int) getZeroOverridden { |
| 11 | return 1; |
| 12 | } |
| 13 | - (int) getZero { |
| 14 | return 0; |
| 15 | } |
| 16 | @end |
| 17 | |
| 18 | @implementation PublicSubClass2 |
| 19 | - (int) getZeroOverridden { |
| 20 | return 0; |
| 21 | } |
| 22 | |
| 23 | /* Test that we get the right type from call to alloc. */ |
| 24 | + (void) testAllocSelf { |
| 25 | id a = [self alloc]; |
| 26 | clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}} |
| 27 | } |
| 28 | |
| 29 | |
| 30 | + (void) testAllocClass { |
| 31 | id a = [PublicSubClass2 alloc]; |
| 32 | clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}} |
| 33 | } |
| 34 | |
| 35 | + (void) testAllocSuperOverriden { |
| 36 | id a = [super alloc]; |
| 37 | // Evaluates to 1 in the parent. |
| 38 | clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{FALSE}} |
| 39 | } |
| 40 | |
| 41 | + (void) testAllocSuper { |
| 42 | id a = [super alloc]; |
| 43 | clang_analyzer_eval([a getZero] == 0); // expected-warning{{TRUE}} |
| 44 | } |
| 45 | |
| 46 | + (void) testAllocInit { |
| 47 | id a = [[self alloc] init]; |
| 48 | clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}} |
| 49 | } |
| 50 | |
| 51 | + (void) testNewSelf { |
| 52 | id a = [self new]; |
| 53 | clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}} |
| 54 | } |
| 55 | |
| 56 | // Casting to parent should not pessimize the dynamic type. |
| 57 | + (void) testCastToParent { |
| 58 | id a = [[self alloc] init]; |
| 59 | PublicParent *p = a; |
| 60 | clang_analyzer_eval([p getZeroOverridden] == 0); // expected-warning{{TRUE}} |
| 61 | } |
| 62 | |
| 63 | // The type of parameter gets used. |
| 64 | + (void)testTypeFromParam:(PublicParent*) p { |
| 65 | clang_analyzer_eval([p getZero] == 0); // expected-warning{{TRUE}} |
| 66 | } |
| 67 | |
| 68 | // Test implicit cast. |
| 69 | // Note, in this case, p could also be a subclass of MyParent. |
| 70 | + (void) testCastFromId:(id) a { |
| 71 | PublicParent *p = a; |
| 72 | clang_analyzer_eval([p getZero] == 0); // expected-warning{{TRUE}} |
| 73 | } |
| 74 | @end |
| 75 | |
| 76 | // TODO: Would be nice to handle the case of dynamically obtained class info |
| 77 | // as well. We need a MemRegion for class types for this. |
| 78 | int testDynamicClass(BOOL coin) { |
| 79 | Class AllocClass = (coin ? [NSObject class] : [PublicSubClass2 class]); |
| 80 | id x = [[AllocClass alloc] init]; |
| 81 | if (coin) |
| 82 | return [x getZero]; |
| 83 | return 1; |
| 84 | } |
| 85 | |
| 86 | @interface UserClass : NSObject |
| 87 | - (PublicSubClass2 *) _newPublicSubClass2; |
| 88 | - (int) getZero; |
| 89 | - (void) callNew; |
| 90 | @end |
| 91 | |
| 92 | @implementation UserClass |
| 93 | - (PublicSubClass2 *) _newPublicSubClass2 { |
| 94 | return [[PublicSubClass2 alloc] init]; |
| 95 | } |
| 96 | - (int) getZero { return 5; } |
| 97 | - (void) callNew { |
| 98 | PublicSubClass2 *x = [self _newPublicSubClass2]; |
| 99 | clang_analyzer_eval([x getZero] == 0); //expected-warning{{TRUE}} |
| 100 | } |
| 101 | @end |