]> granicus.if.org Git - clang/commitdiff
[analyzer] Assume [NSNull null] does not return nil.
authorAnna Zaks <ganna@apple.com>
Fri, 10 May 2013 18:04:46 +0000 (18:04 +0000)
committerAnna Zaks <ganna@apple.com>
Fri, 10 May 2013 18:04:46 +0000 (18:04 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181616 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
test/Analysis/NSContainers.m

index fba14a0fc4981845ea00710b9c390944554b4b5c..6388a8df6487853df256b2f02f0f937c98eb437d 100644 (file)
@@ -58,6 +58,7 @@ enum FoundationClass {
   FC_NSArray,
   FC_NSDictionary,
   FC_NSEnumerator,
+  FC_NSNull,
   FC_NSOrderedSet,
   FC_NSSet,
   FC_NSString
@@ -69,6 +70,7 @@ static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) {
     Classes["NSArray"] = FC_NSArray;
     Classes["NSDictionary"] = FC_NSDictionary;
     Classes["NSEnumerator"] = FC_NSEnumerator;
+    Classes["NSNull"] = FC_NSNull;
     Classes["NSOrderedSet"] = FC_NSOrderedSet;
     Classes["NSSet"] = FC_NSSet;
     Classes["NSString"] = FC_NSString;
@@ -845,6 +847,7 @@ class ObjCNonNilReturnValueChecker
     mutable bool Initialized;
     mutable Selector ObjectAtIndex;
     mutable Selector ObjectAtIndexedSubscript;
+    mutable Selector NullSelector;
 
 public:
   ObjCNonNilReturnValueChecker() : Initialized(false) {}
@@ -870,6 +873,7 @@ void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
     ASTContext &Ctx = C.getASTContext();
     ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
     ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
+    NullSelector = GetNullarySelector("null", Ctx);
   }
 
   // Check the receiver type.
@@ -889,10 +893,11 @@ void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
       State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
     }
 
+    FoundationClass Cl = findKnownClass(Interface);
+
     // Objects returned from
     // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
     // are never 'nil'.
-    FoundationClass Cl = findKnownClass(Interface);
     if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
       Selector Sel = M.getSelector();
       if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
@@ -900,6 +905,14 @@ void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
         State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
       }
     }
+
+    // Objects returned from [NSNull null] are not nil.
+    if (Cl == FC_NSNull) {
+      if (M.getSelector() == NullSelector) {
+        // Go ahead and assume the value is non-nil.
+        State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
+      }
+    }
   }
   C.addTransition(State);
 }
index 540c7a4eabdd6d76abc9e7ef450bb00824b4382c..6b4089b3e529ae5a130a4c7587e13ef3137c323a 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.NilArg -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.NonNilReturnValue,osx.cocoa.NilArg -verify -Wno-objc-root-class %s
 typedef unsigned long NSUInteger;
 typedef signed char BOOL;
 typedef struct _NSZone NSZone;
@@ -81,6 +81,10 @@ typedef struct _NSZone NSZone;
 
 @end
 
+@interface NSNull : NSObject <NSCopying, NSSecureCoding>
++ (NSNull *)null;
+@end
+
 // NSMutableArray API
 void testNilArgNSMutableArray1() {
   NSMutableArray *marray = [[NSMutableArray alloc] init];
@@ -197,4 +201,13 @@ void testNilReceiverRetNil2(NSMutableDictionary *D, Foo *FooPtrIn, id value) {
   [D setObject: value forKey: key]; // no-warning
 }
 
+void testAssumeNSNullNullReturnsNonNil(NSMutableDictionary *Table, id Object,
+                                      id InValue) {
+  id Value = Object ? [Table objectForKey:Object] : [NSNull null];
+  if (!Value) {
+    Value = InValue;
+    [Table setObject:Value forKey:Object]; // no warning
+  }
+}
+