]> granicus.if.org Git - clang/commitdiff
[analyzer] Do not propagate the [super init] could be nil assumption
authorAnna Zaks <ganna@apple.com>
Thu, 30 Aug 2012 19:40:52 +0000 (19:40 +0000)
committerAnna Zaks <ganna@apple.com>
Thu, 30 Aug 2012 19:40:52 +0000 (19:40 +0000)
from callee to caller.

radar://12109638

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@162935 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
test/Analysis/inlining/assume-super-init-does-not-return-nil.m [new file with mode: 0644]

index 13bb3f6a901a4db8428252a3512fd4133af12208..9ba2f8c21aa66d4c8237303117ca7bf1c5a0307a 100644 (file)
@@ -100,6 +100,11 @@ public:
     return Pred->getStackFrame();
   }
 
+  /// Returns true if the predecessor is within an inlined function/method.
+  bool isWithinInlined() {
+    return (getStackFrame() != 0);
+  }
+
   BugReporter &getBugReporter() {
     return Eng.getBugReporter();
   }
index a09324ee6b42935a09f89a966d90012290d35a2e..e5fbb832a92eef0ba5224deba0e6acae31cef030 100644 (file)
@@ -718,8 +718,8 @@ void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
 
 namespace {
 /// \class ObjCNonNilReturnValueChecker
-/// \brief The checker restricts the return values of APIs known to never
-/// return nil.
+/// \brief The checker restricts the return values of APIs known to
+/// never (or almost never) return 'nil'.
 class ObjCNonNilReturnValueChecker
   : public Checker<check::PostObjCMessage> {
     mutable bool Initialized;
@@ -732,9 +732,18 @@ public:
 };
 }
 
+ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
+                                    ProgramStateRef State,
+                                    CheckerContext &C) {
+  SVal Val = State->getSVal(NonNullExpr, C.getLocationContext());
+  if (!isa<DefinedOrUnknownSVal>(Val))
+    return State;
+  return State->assume(cast<DefinedOrUnknownSVal>(Val), true);
+}
+
 void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
                                                         CheckerContext &C)
-                                                         const {
+                                                        const {
   ProgramStateRef State = C.getState();
 
   if (!Initialized) {
@@ -745,19 +754,34 @@ void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
 
   // Check the receiver type.
   if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
+
+    // Assume that object returned from '[self init]' or '[super init]' is not
+    // 'nil' if we are processing an inlined function/method.
+    //
+    // A defensive callee will (and should) check if the object returned by
+    // '[super init]' is 'nil' before doing it's own initialization. However,
+    // since 'nil' is rarely returned in practice, we should not warn when the
+    // caller to the defensive constructor uses the object in contexts where
+    // 'nil' is not accepted.
+    if (C.isWithinInlined() &&
+        M.getDecl()->getMethodFamily() == OMF_init &&
+        M.isReceiverSelfOrSuper()) {
+      State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
+    }
+
+    // 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) {
         // Go ahead and assume the value is non-nil.
-        SVal Val = State->getSVal(M.getOriginExpr(), C.getLocationContext());
-        if (!isa<DefinedOrUnknownSVal>(Val))
-          return;
-        State = State->assume(cast<DefinedOrUnknownSVal>(Val), true);
-        C.addTransition(State);
+        State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
       }
     }
   }
+  C.addTransition(State);
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/test/Analysis/inlining/assume-super-init-does-not-return-nil.m b/test/Analysis/inlining/assume-super-init-does-not-return-nil.m
new file mode 100644 (file)
index 0000000..d17835e
--- /dev/null
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -analyze -analyzer-ipa=dynamic-bifurcate -analyzer-checker=core,osx -verify %s
+
+typedef signed char BOOL;
+
+@protocol NSObject  - (BOOL)isEqual:(id)object; @end
+@interface NSObject <NSObject> {}
++(id)alloc;
++(id)new;
+-(id)init;
+-(id)autorelease;
+-(id)copy;
+- (Class)class;
+-(id)retain;
+- (oneway void)release;
+@end
+
+@interface Cell : NSObject {
+  int x;
+}
+- (id) init;
+- (void)test;
+@end
+
+@implementation Cell
+- (id) init {
+  if ((self = [super init])) {
+    ;
+  }
+  // Test that this is being analyzed.
+  int m;
+  m = m + 1; //expected-warning {{The left operand of '+' is a garbage value}}
+  return self;
+}
+
+// Make sure that we do not propagate the 'nil' check from inlined 'init' to 'test'.
+- (void) test {
+  Cell *newCell = [[Cell alloc] init];
+  newCell->x = 5; // no-warning
+  [newCell release];
+}
+@end