Add static analyzer support for new NSArray/NSDictionary/NSNumber literals.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@152139 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
index f886c3e..6d1da6e 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
@@ -16,6 +16,8 @@
 #define LLVM_CLANG_GR_SVALBUILDER
 
 #include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
@@ -216,6 +218,10 @@
         BasicVals.getValue(integer->getValue(),
                      integer->getType()->isUnsignedIntegerOrEnumerationType()));
   }
+  
+  nonloc::ConcreteInt makeBoolVal(const ObjCBoolLiteralExpr *boolean) {
+    return makeTruthVal(boolean->getValue(), boolean->getType());
+  }
 
   nonloc::ConcreteInt makeBoolVal(const CXXBoolLiteralExpr *boolean);
 
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index 6b14013..f7dd6c2 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -1861,41 +1861,49 @@
   if (!PrevT) {
     const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt();
 
-    if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
-      // Get the name of the callee (if it is available).
-      SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
-      if (const FunctionDecl *FD = X.getAsFunctionDecl())
-        os << "Call to function '" << *FD << '\'';
-      else
-        os << "function call";
+    if (isa<ObjCArrayLiteral>(S)) {
+      os << "NSArray literal is an object with a +0 retain count";
     }
-    else {
-      assert(isa<ObjCMessageExpr>(S));      
-      // The message expression may have between written directly or as
-      // a property access.  Lazily determine which case we are looking at.
-      os << (isPropertyAccess(S, N->getParentMap()) ? "Property" : "Method");
+    else if (isa<ObjCDictionaryLiteral>(S)) {
+      os << "NSDictionary literal is an object with a +0 retain count";
     }
-
-    if (CurrV.getObjKind() == RetEffect::CF) {
-      os << " returns a Core Foundation object with a ";
-    }
-    else {
-      assert (CurrV.getObjKind() == RetEffect::ObjC);
-      os << " returns an Objective-C object with a ";
-    }
-
-    if (CurrV.isOwned()) {
-      os << "+1 retain count";
-
-      if (GCEnabled) {
-        assert(CurrV.getObjKind() == RetEffect::CF);
-        os << ".  "
-        "Core Foundation objects are not automatically garbage collected.";
+    else {      
+      if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
+        // Get the name of the callee (if it is available).
+        SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
+        if (const FunctionDecl *FD = X.getAsFunctionDecl())
+          os << "Call to function '" << *FD << '\'';
+        else
+          os << "function call";
       }
-    }
-    else {
-      assert (CurrV.isNotOwned());
-      os << "+0 retain count";
+      else {
+        assert(isa<ObjCMessageExpr>(S));      
+        // The message expression may have between written directly or as
+        // a property access.  Lazily determine which case we are looking at.
+        os << (isPropertyAccess(S, N->getParentMap()) ? "Property" : "Method");
+      }
+
+      if (CurrV.getObjKind() == RetEffect::CF) {
+        os << " returns a Core Foundation object with a ";
+      }
+      else {
+        assert (CurrV.getObjKind() == RetEffect::ObjC);
+        os << " returns an Objective-C object with a ";
+      }
+
+      if (CurrV.isOwned()) {
+        os << "+1 retain count";
+
+        if (GCEnabled) {
+          assert(CurrV.getObjKind() == RetEffect::CF);
+          os << ".  "
+          "Core Foundation objects are not automatically garbage collected.";
+        }
+      }
+      else {
+        assert (CurrV.isNotOwned());
+        os << "+0 retain count";
+      }
     }
 
     PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
@@ -2295,6 +2303,8 @@
                     check::PostStmt<CastExpr>,
                     check::PostStmt<CallExpr>,
                     check::PostStmt<CXXConstructExpr>,
+                    check::PostStmt<ObjCArrayLiteral>,
+                    check::PostStmt<ObjCDictionaryLiteral>,
                     check::PostObjCMessage,
                     check::PreStmt<ReturnStmt>,
                     check::RegionChanges,
@@ -2439,7 +2449,10 @@
 
   void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
   void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const;
+  void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const;
+  void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;
   void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const;
+                      
   void checkSummary(const RetainSummary &Summ, const CallOrObjCMessage &Call,
                     CheckerContext &C) const;
 
@@ -2474,6 +2487,8 @@
   void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange,
                            RefVal::Kind ErrorKind, SymbolRef Sym,
                            CheckerContext &C) const;
+                      
+  void processObjCLiterals(CheckerContext &C, const Expr *Ex) const;
 
   const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const;
 
@@ -2637,6 +2652,49 @@
   checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C);
 }
 
+void RetainCountChecker::processObjCLiterals(CheckerContext &C,
+                                             const Expr *Ex) const {
+  ProgramStateRef state = C.getState();
+  const ExplodedNode *pred = C.getPredecessor();  
+  for (Stmt::const_child_iterator it = Ex->child_begin(), et = Ex->child_end() ;
+       it != et ; ++it) {
+    const Stmt *child = *it;
+    SVal V = state->getSVal(child, pred->getLocationContext());
+    if (SymbolRef sym = V.getAsSymbol())
+      if (const RefVal* T = state->get<RefBindings>(sym)) {
+        RefVal::Kind hasErr = (RefVal::Kind) 0;
+        state = updateSymbol(state, sym, *T, MayEscape, hasErr, C);
+        if (hasErr) {
+          processNonLeakError(state, child->getSourceRange(), hasErr, sym, C);
+          return;
+        }
+      }
+  }
+  
+  // Return the object as autoreleased.
+  //  RetEffect RE = RetEffect::MakeNotOwned(RetEffect::ObjC);
+  if (SymbolRef sym = 
+        state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) {
+    QualType ResultTy = Ex->getType();
+    state = state->set<RefBindings>(sym, RefVal::makeNotOwned(RetEffect::ObjC,
+                                                              ResultTy));
+  }
+  
+  C.addTransition(state);  
+}
+
+void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL,
+                                       CheckerContext &C) const {
+  // Apply the 'MayEscape' to all values.
+  processObjCLiterals(C, AL);
+}
+
+void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
+                                       CheckerContext &C) const {
+  // Apply the 'MayEscape' to all keys and values.
+  processObjCLiterals(C, DL);
+}
+
 void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg, 
                                               CheckerContext &C) const {
   ProgramStateRef state = C.getState();
diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp
index 5207a50..b5ea3db 100644
--- a/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/lib/StaticAnalyzer/Core/Environment.cpp
@@ -79,6 +79,9 @@
         else
           return svalBuilder.makeIntVal(cast<IntegerLiteral>(E));
       }
+      case Stmt::ObjCBoolLiteralExprClass:
+        return svalBuilder.makeBoolVal(cast<ObjCBoolLiteralExpr>(E));
+
       // For special C0xx nullptr case, make a null pointer SVal.
       case Stmt::CXXNullPtrLiteralExprClass:
         return svalBuilder.makeNull();
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 7528b4a..408bcd3 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -555,6 +555,10 @@
       Bldr.addNodes(Dst);
       break;
 
+    // FIXME.
+    case Stmt::ObjCSubscriptRefExprClass:
+      break;
+      
     case Stmt::ObjCPropertyRefExprClass:
       // Implicitly handled by Environment::getSVal().
       break;
@@ -586,6 +590,7 @@
     case Stmt::ObjCIsaExprClass:
     case Stmt::ObjCProtocolExprClass:
     case Stmt::ObjCSelectorExprClass:
+    case Expr::ObjCNumericLiteralClass:
     case Stmt::ParenListExprClass:
     case Stmt::PredefinedExprClass:
     case Stmt::ShuffleVectorExprClass:
@@ -602,6 +607,7 @@
     case Stmt::IntegerLiteralClass:
     case Stmt::CharacterLiteralClass:
     case Stmt::CXXBoolLiteralExprClass:
+    case Stmt::ObjCBoolLiteralExprClass:
     case Stmt::FloatingLiteralClass:
     case Stmt::SizeOfPackExprClass:
     case Stmt::StringLiteralClass:
@@ -616,6 +622,36 @@
       break;
     }
 
+    case Expr::ObjCArrayLiteralClass:
+    case Expr::ObjCDictionaryLiteralClass: {
+      Bldr.takeNodes(Pred);
+
+      ExplodedNodeSet preVisit;
+      getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this);
+      
+      // FIXME: explicitly model with a region and the actual contents
+      // of the container.  For now, conjure a symbol.
+      ExplodedNodeSet Tmp;
+      StmtNodeBuilder Bldr2(preVisit, Tmp, *currentBuilderContext);
+
+      for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end();
+           it != et; ++it) {      
+        ExplodedNode *N = *it;
+        const Expr *Ex = cast<Expr>(S);
+        QualType resultType = Ex->getType();
+        const LocationContext *LCtx = N->getLocationContext();
+        SVal result =
+          svalBuilder.getConjuredSymbolVal(0, Ex, LCtx, resultType, 
+                                 currentBuilderContext->getCurrentBlockCount());
+        ProgramStateRef state = N->getState()->BindExpr(Ex, LCtx, result);
+        Bldr2.generateNode(S, N, state);
+      }
+      
+      getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
+      Bldr.addNodes(Dst);
+      break;      
+    }
+
     case Stmt::ArraySubscriptExprClass:
       Bldr.takeNodes(Pred);
       VisitLvalArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst);
diff --git a/test/Analysis/objc-arc.m b/test/Analysis/objc-arc.m
index b02af05..a5bf05f 100644
--- a/test/Analysis/objc-arc.m
+++ b/test/Analysis/objc-arc.m
@@ -3,6 +3,7 @@
 typedef signed char BOOL;
 typedef struct _NSZone NSZone;
 @class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator;
+typedef unsigned long NSUInteger;
 
 @protocol NSObject
 - (BOOL)isEqual:(id)object;
@@ -10,12 +11,30 @@
 @protocol NSCopying
 - (id)copyWithZone:(NSZone *)zone;
 @end
-@protocol NSCoding
+@protocol NSCoding;
+@protocol NSMutableCopying;
+@protocol NSFastEnumeration
 - (void)encodeWithCoder:(NSCoder *)aCoder;
 @end
+@protocol NSMutableCopying  - (id)mutableCopyWithZone:(NSZone *)zone;
+@end
+@protocol NSCoding  - (void)encodeWithCoder:(NSCoder *)aCoder;
+@end
 @interface NSObject <NSObject> {}
 + (id)alloc;
+- (id)init;
+- (NSString *)description;
 @end
+@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>
+- (NSUInteger)count;
+- (id)initWithObjects:(const id [])objects count:(NSUInteger)cnt;
++ (id)arrayWithObject:(id)anObject;
++ (id)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt;
++ (id)arrayWithObjects:(id)firstObj, ... __attribute__((sentinel(0,1)));
+- (id)initWithObjects:(id)firstObj, ... __attribute__((sentinel(0,1)));
+- (id)initWithArray:(NSArray *)array;
+@end
+
 typedef const struct __CFAllocator * CFAllocatorRef;
 extern const CFAllocatorRef kCFAllocatorDefault;
 typedef double CFTimeInterval;
@@ -153,3 +172,32 @@
   return x; // no-warning
 }
 
+void test_objc_arrays() {
+    { // CASE ONE -- OBJECT IN ARRAY CREATED DIRECTLY
+        NSObject *o = [[NSObject alloc] init];
+        NSArray *a = [[NSArray alloc] initWithObjects:o, (void*)0];
+        [a description];
+        [o description];
+    }
+
+    { // CASE TWO -- OBJECT IN ARRAY CREATED BY DUPING AUTORELEASED ARRAY
+        NSObject *o = [[NSObject alloc] init];
+        NSArray *a1 = [NSArray arrayWithObjects:o, (void*)0];
+        NSArray *a2 = [[NSArray alloc] initWithArray:a1];
+        [a2 description];
+        [o description];
+    }
+
+    { // CASE THREE -- OBJECT IN RETAINED @[]
+        NSObject *o = [[NSObject alloc] init];
+        NSArray *a3 = @[o];
+        [a3 description];
+        [o description];
+    }
+    {
+      // CASE 4, verify analyzer still working.
+      CFCreateString(); // expected-warning {{leak}}
+    }
+}
+
+
diff --git a/test/Analysis/objc-bool.m b/test/Analysis/objc-bool.m
new file mode 100644
index 0000000..631cd2d
--- /dev/null
+++ b/test/Analysis/objc-bool.m
@@ -0,0 +1,22 @@
+// RUN: %clang --analyze %s -o %t -verify
+
+// Test handling of ObjC bool literals.
+
+typedef signed char BOOL;
+
+void rdar_10597458() {
+  if (__objc_yes)
+    return;
+  int *p = 0;
+  *p = 0xDEADBEEF; // no-warning
+}
+
+void rdar_10597458_b(BOOL b) {
+  if (b == __objc_no)
+    return;
+  
+  if (b == __objc_no) {
+    int *p = 0;
+    *p = 0xDEADBEEF; // no-warning
+  }
+}
diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m
index 1ca4876..548564f 100644
--- a/test/Analysis/retain-release.m
+++ b/test/Analysis/retain-release.m
@@ -115,10 +115,15 @@
 - (id)retain;
 - (oneway void)release;
 - (id)autorelease;
+- (NSString *)description;
 - (id)init;
-@end  @protocol NSCopying  - (id)copyWithZone:(NSZone *)zone;
-@end  @protocol NSMutableCopying  - (id)mutableCopyWithZone:(NSZone *)zone;
-@end  @protocol NSCoding  - (void)encodeWithCoder:(NSCoder *)aCoder;
+@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)allocWithZone:(NSZone *)zone;
@@ -132,13 +137,22 @@
 typedef struct {
 }
 NSFastEnumerationState;
-@protocol NSFastEnumeration  - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len;
-@end           @class NSString, NSDictionary;
+@protocol NSFastEnumeration 
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len;
+@end
+@class NSString, NSDictionary;
 @interface NSValue : NSObject <NSCopying, NSCoding>  - (void)getValue:(void *)value;
 @end  @interface NSNumber : NSValue  - (char)charValue;
 - (id)initWithInt:(int)value;
 @end   @class NSString;
-@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>  - (NSUInteger)count;
+@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>
+- (NSUInteger)count;
+- (id)initWithObjects:(const id [])objects count:(NSUInteger)cnt;
++ (id)arrayWithObject:(id)anObject;
++ (id)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt;
++ (id)arrayWithObjects:(id)firstObj, ... __attribute__((sentinel(0,1)));
+- (id)initWithObjects:(id)firstObj, ... __attribute__((sentinel(0,1)));
+- (id)initWithArray:(NSArray *)array;
 @end  @interface NSArray (NSArrayCreation)  + (id)array;
 @end       @interface NSAutoreleasePool : NSObject {
 }
@@ -158,8 +172,12 @@
 + (id)dataWithBytesNoCopy:(void *)bytes length:(NSUInteger)length;
 + (id)dataWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b;
 @end   @class NSLocale, NSDate, NSCalendar, NSTimeZone, NSError, NSArray, NSMutableDictionary;
-@interface NSDictionary : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>  - (NSUInteger)count;
-@end    @interface NSMutableDictionary : NSDictionary  - (void)removeObjectForKey:(id)aKey;
+@interface NSDictionary : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>
+- (NSUInteger)count;
++ (id)dictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id <NSCopying> [])keys count:(NSUInteger)cnt;
+@end
+@interface NSMutableDictionary : NSDictionary  - (void)removeObjectForKey:(id)aKey;
 - (void)setObject:(id)anObject forKey:(id)aKey;
 @end  @interface NSMutableDictionary (NSMutableDictionaryCreation)  + (id)dictionaryWithCapacity:(NSUInteger)numItems;
 @end  typedef double CGFloat;
@@ -1629,3 +1647,52 @@
   xpc_release(xpc);
 }
 
+//===----------------------------------------------------------------------===//
+// ObjC literals support.
+//===----------------------------------------------------------------------===//
+
+void test_objc_arrays() {
+    { // CASE ONE -- OBJECT IN ARRAY CREATED DIRECTLY
+        NSObject *o = [[NSObject alloc] init];
+        NSArray *a = [[NSArray alloc] initWithObjects:o, (void*)0]; // expected-warning {{leak}}
+        [o release];
+        [a description];
+        [o description];
+    }
+
+    { // CASE TWO -- OBJECT IN ARRAY CREATED BY DUPING AUTORELEASED ARRAY
+        NSObject *o = [[NSObject alloc] init];
+        NSArray *a1 = [NSArray arrayWithObjects:o, (void*)0];
+        NSArray *a2 = [[NSArray alloc] initWithArray:a1]; // expected-warning {{leak}}
+        [o release];        
+        [a2 description];
+        [o description];
+    }
+
+    { // CASE THREE -- OBJECT IN RETAINED @[]
+        NSObject *o = [[NSObject alloc] init];
+        NSArray *a3 = [@[o] retain]; // expected-warning {{leak}}
+        [o release];        
+        [a3 description];
+        [o description];
+    }
+    
+    { // CASE FOUR -- OBJECT IN ARRAY CREATED BY DUPING @[]
+        NSObject *o = [[NSObject alloc] init];
+        NSArray *a = [[NSArray alloc] initWithArray:@[o]]; // expected-warning {{leak}}
+        [o release];
+        
+        [a description];
+        [o description];
+    }
+    
+    { // CASE FIVE -- OBJECT IN RETAINED @{}
+        NSValue *o = [[NSValue alloc] init];
+        NSDictionary *a = [@{o : o} retain]; // expected-warning {{leak}}
+        [o release];
+        
+        [a description];
+        [o description];
+    }
+}
+
diff --git a/tools/scan-build/ccc-analyzer b/tools/scan-build/ccc-analyzer
index 1b273b8..32a7301 100755
--- a/tools/scan-build/ccc-analyzer
+++ b/tools/scan-build/ccc-analyzer
@@ -345,7 +345,8 @@
 );
 
 my %LinkerOptionMap = (
-  '-framework' => 1
+  '-framework' => 1,
+  '-fobjc-link-runtime' => 0
 );
 
 my %CompilerLinkerOptionMap = (