| // RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core -analyzer-store=region -analyzer-constraints=range -verify -fblocks %s -analyzer-eagerly-assume |
| |
| // Delta-reduced header stuff (needed for test cases). |
| typedef signed char BOOL; |
| typedef unsigned int NSUInteger; |
| typedef struct _NSZone NSZone; |
| @class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator; |
| @protocol NSObject - (BOOL)isEqual:(id)object; |
| - (oneway void)release; |
| @end @protocol NSCopying - (id)copyWithZone:(NSZone *)zone; |
| @end @protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; |
| @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; |
| @end @interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration> - (NSUInteger)count; |
| @end @interface NSMutableArray : NSArray - (void)addObject:(id)anObject; |
| - (BOOL)isEqualToString:(NSString *)aString; |
| @end @interface NSAutoreleasePool : NSObject {} |
| - (void)drain; |
| - (id)init; |
| @end |
| |
| // This test case tests that (x != 0) is eagerly evaluated before stored to |
| // 'y'. This test case complements recoverCastedSymbol (see below) because |
| // the symbolic expression is stored to 'y' (which is a short instead of an |
| // int). recoverCastedSymbol() only recovers path-sensitivity when the |
| // symbolic expression is literally the branch condition. |
| // |
| void handle_assign_of_condition(int x) { |
| // The cast to 'short' causes us to lose symbolic constraint. |
| short y = (x != 0); |
| char *p = 0; |
| if (y) { |
| // This should be infeasible. |
| if (!(x != 0)) { |
| *p = 1; // no-warning |
| } |
| } |
| } |
| |
| // From <rdar://problem/6619921> |
| // |
| // In this test case, 'needsAnArray' is a signed char. The analyzer tracks |
| // a symbolic value for this variable, but in the branch condition it is |
| // promoted to 'int'. Currently the analyzer doesn't reason well about |
| // promotions of symbolic values, so this test case tests the logic in |
| // 'recoverCastedSymbol()' (GRExprEngine.cpp) to test that we recover |
| // path-sensitivity and use the symbol for 'needsAnArray' in the branch |
| // condition. |
| // |
| void handle_symbolic_cast_in_condition(void) { |
| NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
| |
| BOOL needsAnArray = [@"aString" isEqualToString:@"anotherString"]; |
| NSMutableArray* array = needsAnArray ? [[NSMutableArray alloc] init] : 0; |
| if(needsAnArray) |
| [array release]; |
| |
| [pool drain]; |
| } |
| |
| // From PR 3836 (http://llvm.org/bugs/show_bug.cgi?id=3836) |
| // |
| // In this test case, the double '!' works fine with our symbolic constraints, |
| // but we don't support comparing SymConstraint != SymConstraint. By eagerly |
| // assuming the truth of !!a or !!b, we can compare these values directly. |
| // |
| void pr3836(int *a, int *b) { |
| if (!!a != !!b) /* one of them is NULL */ |
| return; |
| if (!a && !b) /* both are NULL */ |
| return; |
| |
| *a = 1; // no-warning |
| *b = 1; // no-warning |
| } |
| |
| |
| //===---------------------------------------------------------------------===// |
| // <rdar://problem/7342806> |
| // This false positive occurred because the symbolic constraint on a short was |
| // not maintained via sign extension. The analyzer doesn't properly handle |
| // the sign extension, but now tracks the constraint. This particular |
| // case relies on -analyzer-eagerly-assume because of the expression |
| // 'Flag1 != Count > 0'. |
| //===---------------------------------------------------------------------===// |
| |
| void rdar7342806_aux(short x); |
| |
| void rdar7342806() { |
| extern short Count; |
| extern short Flag1; |
| |
| short *Pointer = 0; |
| short Flag2 = !!Pointer; // Flag2 is false (0). |
| short Ok = 1; |
| short Which; |
| |
| if( Flag1 != Count > 0 ) |
| // Static analyzer skips this so either |
| // Flag1 is true and Count > 0 |
| // or |
| // Flag1 is false and Count <= 0 |
| Ok = 0; |
| |
| if( Flag1 != Flag2 ) |
| // Analyzer skips this so Flag1 and Flag2 have the |
| // same value, both are false because Flag2 is false. And |
| // from that we know Count must be <= 0. |
| Ok = 0; |
| |
| for( Which = 0; |
| Which < Count && Ok; |
| Which++ ) |
| // This statement can only execute if Count > 0 which can only |
| // happen when Flag1 and Flag2 are both true and Flag2 will only |
| // be true when Pointer is not NULL. |
| rdar7342806_aux(*Pointer); // no-warning |
| } |
| |
| //===---------------------------------------------------------------------===// |
| // PR 5627 - http://llvm.org/bugs/show_bug.cgi?id=5627 |
| // This test case depends on using -analyzer-eagerly-assume and |
| // -analyzer-store=region. The '-analyzer-eagerly-assume' causes the path |
| // to bifurcate when evaluating the function call argument, and a state |
| // caching bug in GRExprEngine::CheckerVisit (and friends) caused the store |
| // to 'p' to not be evaluated along one path, but then an autotransition caused |
| // the path to keep on propagating with 'p' still set to an undefined value. |
| // We would then get a bogus report of returning uninitialized memory. |
| // Note: CheckerVisit mistakenly cleared an existing node, and the cleared |
| // node was resurrected by GRStmtNodeBuilder::~GRStmtNodeBuilder(), where |
| // 'p' was not assigned. |
| //===---------------------------------------------------------------------===// |
| |
| float *pr5627_f(int y); |
| |
| float *pr5627_g(int x) { |
| float *p; |
| p = pr5627_f(!x); |
| return p; // no-warning |
| } |
| |